aboutsummaryrefslogtreecommitdiffhomepage
path: root/video
diff options
context:
space:
mode:
authorGravatar wm4 <wm4@nowhere>2012-11-05 17:02:04 +0100
committerGravatar wm4 <wm4@nowhere>2012-11-12 20:06:14 +0100
commitd4bdd0473d6f43132257c9fb3848d829755167a3 (patch)
tree8021c2f7da1841393c8c832105e20cd527826d6c /video
parentbd48deba77bd5582c5829d6fe73a7d2571088aba (diff)
Rename directories, move files (step 1 of 2) (does not compile)
Tis drops the silly lib prefixes, and attempts to organize the tree in a more logical way. Make the top-level directory less cluttered as well. Renames the following directories: libaf -> audio/filter libao2 -> audio/out libvo -> video/out libmpdemux -> demux Split libmpcodecs: vf* -> video/filter vd*, dec_video.* -> video/decode mp_image*, img_format*, ... -> video/ ad*, dec_audio.* -> audio/decode libaf/format.* is moved to audio/ - this is similar to how mp_image.* is located in video/. Move most top-level .c/.h files to core. (talloc.c/.h is left on top- level, because it's external.) Park some of the more annoying files in compat/. Some of these are relicts from the time mplayer used ffmpeg internals. sub/ is not split, because it's too much of a mess (subtitle code is mixed with OSD display and rendering). Maybe the organization of core is not ideal: it mixes playback core (like mplayer.c) and utility helpers (like bstr.c/h). Should the need arise, the playback core will be moved somewhere else, while core contains all helper and common code.
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"),
+ DEF_FN_NAMES(ActiveTexture, "glActiveTexture", "glActiveTextureARB"),
+ DEF_FN_NAMES(BindTexture, "glBindTexture", "glBindTextureARB", "glBindTextureEXT"),
+ DEF_FN_NAMES(MultiTexCoord2f, "glMultiTexCoord2f", "glMultiTexCoord2fARB"),
+ DEF_FN_NAMES(TexImage3D, "glTexImage3D"),
+ {0}
+ },
+ },
+ // Ancient ARB shaders.
+ {
+ .extension = "_program",
+ .ver_removed = MPGL_VER(3, 0),
+ .functions = (struct gl_function[]) {
+ DEF_FN_NAMES(GenPrograms, "glGenProgramsARB"),
+ DEF_FN_NAMES(DeletePrograms, "glDeleteProgramsARB"),
+ DEF_FN_NAMES(BindProgram, "glBindProgramARB"),
+ DEF_FN_NAMES(ProgramString, "glProgramStringARB"),
+ DEF_FN_NAMES(GetProgramivARB, "glGetProgramivARB"),
+ DEF_FN_NAMES(ProgramEnvParameter4f, "glProgramEnvParameter4fARB"),
+ {0}
+ },
+ },
+ // Ancient ATI extensions.
+ {
+ .extension = "ATI_fragment_shader",
+ .ver_removed = MPGL_VER(3, 0),
+ .functions = (struct gl_function[]) {
+ DEF_FN_NAMES(BeginFragmentShader, "glBeginFragmentShaderATI"),
+ DEF_FN_NAMES(EndFragmentShader, "glEndFragmentShaderATI"),
+ DEF_FN_NAMES(SampleMap, "glSampleMapATI"),
+ DEF_FN_NAMES(ColorFragmentOp2, "glColorFragmentOp2ATI"),
+ DEF_FN_NAMES(ColorFragmentOp3, "glColorFragmentOp3ATI"),
+ DEF_FN_NAMES(SetFragmentShaderConstant, "glSetFragmentShaderConstantATI"),
+ {0}
+ },
+ },
+};
+
+#undef FN_OFFS
+#undef DEF_FN_HARD
+#undef DEF_FN
+#undef DEF_FN_NAMES
+
+
+/**
+ * \brief find the function pointers of some useful OpenGL extensions
+ * \param getProcAddress function to resolve function names, may be NULL
+ * \param ext2 an extra extension string
+ */
+static void getFunctions(GL *gl, void *(*getProcAddress)(const GLubyte *),
+ const char *ext2, bool gl3)
+{
+ talloc_free_children(gl);
+ *gl = (GL) {
+ .extensions = talloc_strdup(gl, ext2 ? ext2 : ""),
+ };
+
+ if (!getProcAddress)
+ getProcAddress = (void *)getdladdr;
+
+ GLint major = 0, minor = 0;
+ if (gl3) {
+ gl->GetStringi = getProcAddress("glGetStringi");
+ gl->GetIntegerv = getProcAddress("glGetIntegerv");
+
+ if (!(gl->GetStringi && gl->GetIntegerv))
+ return;
+
+ gl->GetIntegerv(GL_MAJOR_VERSION, &major);
+ gl->GetIntegerv(GL_MINOR_VERSION, &minor);
+
+ GLint exts;
+ gl->GetIntegerv(GL_NUM_EXTENSIONS, &exts);
+ for (int n = 0; n < exts; n++) {
+ gl->extensions
+ = talloc_asprintf_append(gl->extensions, " %s",
+ gl->GetStringi(GL_EXTENSIONS, n));
+ }
+ } else {
+ gl->GetString = getProcAddress("glGetString");
+ if (!gl->GetString)
+ gl->GetString = glGetString;
+
+ const char *ext = (char*)gl->GetString(GL_EXTENSIONS);
+ gl->extensions = talloc_asprintf_append(gl->extensions, " %s", ext);
+
+ const char *version = gl->GetString(GL_VERSION);
+ sscanf(version, "%d.%d", &major, &minor);
+ }
+ gl->version = MPGL_VER(major, minor);
+
+ mp_msg(MSGT_VO, MSGL_V, "[gl] Detected OpenGL %d.%d.\n", major, minor);
+ mp_msg(MSGT_VO, MSGL_DBG2, "[gl] Combined OpenGL extensions string:\n%s\n",
+ gl->extensions);
+
+ for (int n = 0; n < sizeof(gl_functions) / sizeof(gl_functions[0]); n++) {
+ struct gl_functions *section = &gl_functions[n];
+
+ // With gl3=false, we could have a legacy context, where functionality
+ // is never removed. (E.g. the context could be at version >= 3.0, but
+ // legacy functions like glBegin still exist and work.)
+ if (gl3 && section->ver_removed && gl->version >= section->ver_removed)
+ continue;
+
+ bool must_exist = section->ver_core && gl->version >= section->ver_core
+ && !section->partial_ok;
+
+ if (!must_exist && section->extension &&
+ !strstr(gl->extensions, section->extension))
+ continue;
+
+ void *loaded[MAX_FN_COUNT] = {0};
+ bool all_loaded = true;
+
+ for (int i = 0; section->functions[i].funcnames[0]; i++) {
+ struct gl_function *fn = &section->functions[i];
+ void *ptr = NULL;
+ for (int x = 0; fn->funcnames[x]; x++) {
+ ptr = getProcAddress((const GLubyte *)fn->funcnames[x]);
+ if (ptr)
+ break;
+ }
+ if (!ptr)
+ ptr = fn->fallback;
+ if (!ptr) {
+ all_loaded = false;
+ if (must_exist) {
+ // Either we or the driver are not conforming to OpenGL.
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] Required function '%s' not "
+ "found.\n", fn->funcnames[0]);
+ talloc_free_children(gl);
+ *gl = (GL) {0};
+ return;
+ }
+ }
+ assert(i < MAX_FN_COUNT);
+ loaded[i] = ptr;
+ }
+
+ if (all_loaded || section->partial_ok) {
+ gl->mpgl_caps |= section->provides;
+ for (int i = 0; section->functions[i].funcnames[0]; i++) {
+ struct gl_function *fn = &section->functions[i];
+ void **funcptr = (void**)(((char*)gl) + fn->offset);
+ if (loaded[i])
+ *funcptr = loaded[i];
+ }
+ }
+ }
+
+ gl->glsl_version = 0;
+ if (gl->version >= MPGL_VER(2, 0))
+ gl->glsl_version = 110;
+ if (gl->version >= MPGL_VER(2, 1))
+ gl->glsl_version = 120;
+ if (gl->version >= MPGL_VER(3, 0))
+ gl->glsl_version = 130;
+ // Specifically needed for OSX (normally we request 3.0 contexts only, but
+ // OSX always creates 3.2 contexts when requesting a core context).
+ if (gl->version >= MPGL_VER(3, 2))
+ gl->glsl_version = 150;
+
+ if (!is_software_gl(gl))
+ gl->mpgl_caps |= MPGL_CAP_NO_SW;
+
+ mp_msg(MSGT_VO, MSGL_V, "[gl] Detected OpenGL features:");
+ list_features(gl->mpgl_caps, MSGL_V, false);
+}
+
+/**
+ * \brief create a texture and set some defaults
+ * \param target texture taget, usually GL_TEXTURE_2D
+ * \param fmt internal texture format
+ * \param format texture host data format
+ * \param type texture host data type
+ * \param filter filter used for scaling, e.g. GL_LINEAR
+ * \param w texture width
+ * \param h texture height
+ * \param val luminance value to fill texture with
+ * \ingroup gltexture
+ */
+void glCreateClearTex(GL *gl, GLenum target, GLenum fmt, GLenum format,
+ GLenum type, GLint filter, int w, int h,
+ unsigned char val)
+{
+ GLfloat fval = (GLfloat)val / 255.0;
+ GLfloat border[4] = {
+ fval, fval, fval, fval
+ };
+ int stride;
+ char *init;
+ if (w == 0)
+ w = 1;
+ if (h == 0)
+ h = 1;
+ stride = w * glFmt2bpp(format, type);
+ if (!stride)
+ return;
+ init = malloc(stride * h);
+ memset(init, val, stride * h);
+ glAdjustAlignment(gl, stride);
+ gl->PixelStorei(GL_UNPACK_ROW_LENGTH, w);
+ gl->TexImage2D(target, 0, fmt, w, h, 0, format, type, init);
+ gl->TexParameterf(target, GL_TEXTURE_PRIORITY, 1.0);
+ gl->TexParameteri(target, GL_TEXTURE_MIN_FILTER, filter);
+ gl->TexParameteri(target, GL_TEXTURE_MAG_FILTER, filter);
+ gl->TexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl->TexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ // Border texels should not be used with CLAMP_TO_EDGE
+ // We set a sane default anyway.
+ gl->TexParameterfv(target, GL_TEXTURE_BORDER_COLOR, border);
+ free(init);
+}
+
+static GLint detect_hqtexfmt(GL *gl)
+{
+ const char *extensions = (const char *)gl->GetString(GL_EXTENSIONS);
+ if (strstr(extensions, "_texture_float"))
+ return GL_RGB32F;
+ else if (strstr(extensions, "NV_float_buffer"))
+ return GL_FLOAT_RGB32_NV;
+ return GL_RGB16;
+}
+
+/**
+ * \brief creates a texture from a PPM file
+ * \param target texture taget, usually GL_TEXTURE_2D
+ * \param fmt internal texture format, 0 for default
+ * \param filter filter used for scaling, e.g. GL_LINEAR
+ * \param f file to read PPM from
+ * \param width [out] width of texture
+ * \param height [out] height of texture
+ * \param maxval [out] maxval value from PPM file
+ * \return 0 on error, 1 otherwise
+ * \ingroup gltexture
+ */
+int glCreatePPMTex(GL *gl, GLenum target, GLenum fmt, GLint filter,
+ FILE *f, int *width, int *height, int *maxval)
+{
+ int w, h, m, bpp;
+ GLenum type;
+ uint8_t *data = read_pnm(f, &w, &h, &bpp, &m);
+ GLint hqtexfmt = detect_hqtexfmt(gl);
+ if (!data || (bpp != 3 && bpp != 6)) {
+ free(data);
+ return 0;
+ }
+ if (!fmt) {
+ fmt = bpp == 6 ? hqtexfmt : 3;
+ if (fmt == GL_FLOAT_RGB32_NV && target != GL_TEXTURE_RECTANGLE)
+ fmt = GL_RGB16;
+ }
+ type = bpp == 6 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_BYTE;
+ glCreateClearTex(gl, target, fmt, GL_RGB, type, filter, w, h, 0);
+ glUploadTex(gl, target, GL_RGB, type,
+ data, w * bpp, 0, 0, w, h, 0);
+ free(data);
+ if (width)
+ *width = w;
+ if (height)
+ *height = h;
+ if (maxval)
+ *maxval = m;
+ return 1;
+}
+
+/**
+ * \brief return the number of bytes per pixel for the given format
+ * \param format OpenGL format
+ * \param type OpenGL type
+ * \return bytes per pixel
+ * \ingroup glgeneral
+ *
+ * Does not handle all possible variants, just those used by MPlayer
+ */
+int glFmt2bpp(GLenum format, GLenum type)
+{
+ int component_size = 0;
+ switch (type) {
+ case GL_UNSIGNED_BYTE_3_3_2:
+ case GL_UNSIGNED_BYTE_2_3_3_REV:
+ return 1;
+ case GL_UNSIGNED_SHORT_5_5_5_1:
+ case GL_UNSIGNED_SHORT_1_5_5_5_REV:
+ case GL_UNSIGNED_SHORT_5_6_5:
+ case GL_UNSIGNED_SHORT_5_6_5_REV:
+ return 2;
+ case GL_UNSIGNED_BYTE:
+ component_size = 1;
+ break;
+ case GL_UNSIGNED_SHORT:
+ component_size = 2;
+ break;
+ }
+ switch (format) {
+ case GL_LUMINANCE:
+ case GL_ALPHA:
+ return component_size;
+ case GL_YCBCR_MESA:
+ return 2;
+ case GL_RGB:
+ case GL_BGR:
+ return 3 * component_size;
+ case GL_RGBA:
+ case GL_BGRA:
+ return 4 * component_size;
+ case GL_RED:
+ return component_size;
+ case GL_RG:
+ case GL_LUMINANCE_ALPHA:
+ return 2 * component_size;
+ }
+ abort(); // unknown
+}
+
+/**
+ * \brief upload a texture, handling things like stride and slices
+ * \param target texture target, usually GL_TEXTURE_2D
+ * \param format OpenGL format of data
+ * \param type OpenGL type of data
+ * \param dataptr data to upload
+ * \param stride data stride
+ * \param x x offset in texture
+ * \param y y offset in texture
+ * \param w width of the texture part to upload
+ * \param h height of the texture part to upload
+ * \param slice height of an upload slice, 0 for all at once
+ * \ingroup gltexture
+ */
+void glUploadTex(GL *gl, GLenum target, GLenum format, GLenum type,
+ const void *dataptr, int stride,
+ int x, int y, int w, int h, int slice)
+{
+ const uint8_t *data = dataptr;
+ int y_max = y + h;
+ if (w <= 0 || h <= 0)
+ return;
+ if (slice <= 0)
+ slice = h;
+ if (stride < 0) {
+ data += (h - 1) * stride;
+ stride = -stride;
+ }
+ // this is not always correct, but should work for MPlayer
+ glAdjustAlignment(gl, stride);
+ gl->PixelStorei(GL_UNPACK_ROW_LENGTH, stride / glFmt2bpp(format, type));
+ for (; y + slice <= y_max; y += slice) {
+ gl->TexSubImage2D(target, 0, x, y, w, slice, format, type, data);
+ data += stride * slice;
+ }
+ if (y < y_max)
+ gl->TexSubImage2D(target, 0, x, y, w, y_max - y, format, type, data);
+}
+
+// Like glUploadTex, but upload a byte array with all elements set to val.
+// If scratch is not NULL, points to a resizeable talloc memory block than can
+// be freely used by the function (for avoiding temporary memory allocations).
+void glClearTex(GL *gl, GLenum target, GLenum format, GLenum type,
+ int x, int y, int w, int h, uint8_t val, void **scratch)
+{
+ int bpp = glFmt2bpp(format, type);
+ int stride = w * bpp;
+ int size = h * stride;
+ if (size < 1)
+ return;
+ void *data = scratch ? *scratch : NULL;
+ if (talloc_get_size(data) < size)
+ data = talloc_realloc(NULL, data, char *, size);
+ memset(data, val, size);
+ glAdjustAlignment(gl, stride);
+ gl->PixelStorei(GL_UNPACK_ROW_LENGTH, w);
+ gl->TexSubImage2D(target, 0, x, y, w, h, format, type, data);
+ if (scratch) {
+ *scratch = data;
+ } else {
+ talloc_free(data);
+ }
+}
+
+/**
+ * \brief download a texture, handling things like stride and slices
+ * \param target texture target, usually GL_TEXTURE_2D
+ * \param format OpenGL format of data
+ * \param type OpenGL type of data
+ * \param dataptr destination memory for download
+ * \param stride data stride (must be positive)
+ * \ingroup gltexture
+ */
+void glDownloadTex(GL *gl, GLenum target, GLenum format, GLenum type,
+ void *dataptr, int stride)
+{
+ // this is not always correct, but should work for MPlayer
+ glAdjustAlignment(gl, stride);
+ gl->PixelStorei(GL_PACK_ROW_LENGTH, stride / glFmt2bpp(format, type));
+ gl->GetTexImage(target, 0, format, type, dataptr);
+}
+
+/**
+ * \brief Setup ATI version of register combiners for YUV to RGB conversion.
+ * \param csp_params parameters used for colorspace conversion
+ * \param text if set use the GL_ATI_text_fragment_shader API as
+ * used on OS X.
+ */
+static void glSetupYUVFragmentATI(GL *gl, struct mp_csp_params *csp_params,
+ int text)
+{
+ GLint i;
+ float yuv2rgb[3][4];
+
+ gl->GetIntegerv(GL_MAX_TEXTURE_UNITS, &i);
+ if (i < 3)
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "[gl] 3 texture units needed for YUV combiner (ATI) support (found %i)\n", i);
+
+ mp_get_yuv2rgb_coeffs(csp_params, yuv2rgb);
+ for (i = 0; i < 3; i++) {
+ int j;
+ yuv2rgb[i][3] -= -0.5 * (yuv2rgb[i][1] + yuv2rgb[i][2]);
+ for (j = 0; j < 4; j++) {
+ yuv2rgb[i][j] *= 0.125;
+ yuv2rgb[i][j] += 0.5;
+ if (yuv2rgb[i][j] > 1)
+ yuv2rgb[i][j] = 1;
+ if (yuv2rgb[i][j] < 0)
+ yuv2rgb[i][j] = 0;
+ }
+ }
+ if (text == 0) {
+ GLfloat c0[4] = { yuv2rgb[0][0], yuv2rgb[1][0], yuv2rgb[2][0] };
+ GLfloat c1[4] = { yuv2rgb[0][1], yuv2rgb[1][1], yuv2rgb[2][1] };
+ GLfloat c2[4] = { yuv2rgb[0][2], yuv2rgb[1][2], yuv2rgb[2][2] };
+ GLfloat c3[4] = { yuv2rgb[0][3], yuv2rgb[1][3], yuv2rgb[2][3] };
+ if (!gl->BeginFragmentShader || !gl->EndFragmentShader ||
+ !gl->SetFragmentShaderConstant || !gl->SampleMap ||
+ !gl->ColorFragmentOp2 || !gl->ColorFragmentOp3) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Combiner (ATI) functions missing!\n");
+ return;
+ }
+ gl->GetIntegerv(GL_NUM_FRAGMENT_REGISTERS_ATI, &i);
+ if (i < 3)
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "[gl] 3 registers needed for YUV combiner (ATI) support (found %i)\n", i);
+ gl->BeginFragmentShader();
+ gl->SetFragmentShaderConstant(GL_CON_0_ATI, c0);
+ gl->SetFragmentShaderConstant(GL_CON_1_ATI, c1);
+ gl->SetFragmentShaderConstant(GL_CON_2_ATI, c2);
+ gl->SetFragmentShaderConstant(GL_CON_3_ATI, c3);
+ gl->SampleMap(GL_REG_0_ATI, GL_TEXTURE0, GL_SWIZZLE_STR_ATI);
+ gl->SampleMap(GL_REG_1_ATI, GL_TEXTURE1, GL_SWIZZLE_STR_ATI);
+ gl->SampleMap(GL_REG_2_ATI, GL_TEXTURE2, GL_SWIZZLE_STR_ATI);
+ gl->ColorFragmentOp2(GL_MUL_ATI, GL_REG_1_ATI, GL_NONE, GL_NONE,
+ GL_REG_1_ATI, GL_NONE, GL_BIAS_BIT_ATI,
+ GL_CON_1_ATI, GL_NONE, GL_BIAS_BIT_ATI);
+ gl->ColorFragmentOp3(GL_MAD_ATI, GL_REG_2_ATI, GL_NONE, GL_NONE,
+ GL_REG_2_ATI, GL_NONE, GL_BIAS_BIT_ATI,
+ GL_CON_2_ATI, GL_NONE, GL_BIAS_BIT_ATI,
+ GL_REG_1_ATI, GL_NONE, GL_NONE);
+ gl->ColorFragmentOp3(GL_MAD_ATI, GL_REG_0_ATI, GL_NONE, GL_NONE,
+ GL_REG_0_ATI, GL_NONE, GL_NONE,
+ GL_CON_0_ATI, GL_NONE, GL_BIAS_BIT_ATI,
+ GL_REG_2_ATI, GL_NONE, GL_NONE);
+ gl->ColorFragmentOp2(GL_ADD_ATI, GL_REG_0_ATI, GL_NONE, GL_8X_BIT_ATI,
+ GL_REG_0_ATI, GL_NONE, GL_NONE,
+ GL_CON_3_ATI, GL_NONE, GL_BIAS_BIT_ATI);
+ gl->EndFragmentShader();
+ } else {
+ static const char template[] =
+ "!!ATIfs1.0\n"
+ "StartConstants;\n"
+ " CONSTANT c0 = {%e, %e, %e};\n"
+ " CONSTANT c1 = {%e, %e, %e};\n"
+ " CONSTANT c2 = {%e, %e, %e};\n"
+ " CONSTANT c3 = {%e, %e, %e};\n"
+ "EndConstants;\n"
+ "StartOutputPass;\n"
+ " SampleMap r0, t0.str;\n"
+ " SampleMap r1, t1.str;\n"
+ " SampleMap r2, t2.str;\n"
+ " MUL r1.rgb, r1.bias, c1.bias;\n"
+ " MAD r2.rgb, r2.bias, c2.bias, r1;\n"
+ " MAD r0.rgb, r0, c0.bias, r2;\n"
+ " ADD r0.rgb.8x, r0, c3.bias;\n"
+ "EndPass;\n";
+ char buffer[512];
+ snprintf(buffer, sizeof(buffer), template,
+ yuv2rgb[0][0], yuv2rgb[1][0], yuv2rgb[2][0],
+ yuv2rgb[0][1], yuv2rgb[1][1], yuv2rgb[2][1],
+ yuv2rgb[0][2], yuv2rgb[1][2], yuv2rgb[2][2],
+ yuv2rgb[0][3], yuv2rgb[1][3], yuv2rgb[2][3]);
+ mp_msg(MSGT_VO, MSGL_DBG2, "[gl] generated fragment program:\n%s\n",
+ buffer);
+ loadGPUProgram(gl, GL_TEXT_FRAGMENT_SHADER_ATI, buffer);
+ }
+}
+
+// Replace all occurances of variables named "$"+name (e.g. $foo) in *text with
+// replace, and return the result. *text must have been allocated with talloc.
+static void replace_var_str(char **text, const char *name, const char *replace)
+{
+ size_t namelen = strlen(name);
+ char *nextvar = *text;
+ void *parent = talloc_parent(*text);
+ for (;;) {
+ nextvar = strchr(nextvar, '$');
+ if (!nextvar)
+ break;
+ char *until = nextvar;
+ nextvar++;
+ if (strncmp(nextvar, name, namelen) != 0)
+ continue;
+ nextvar += namelen;
+ // try not to replace prefixes of other vars (e.g. $foo vs. $foo_bar)
+ char term = nextvar[0];
+ if (isalnum(term) || term == '_')
+ continue;
+ int prelength = until - *text;
+ int postlength = nextvar - *text;
+ char *n = talloc_asprintf(parent, "%.*s%s%s", prelength, *text, replace,
+ nextvar);
+ talloc_free(*text);
+ *text = n;
+ nextvar = *text + postlength;
+ }
+}
+
+static void replace_var_float(char **text, const char *name, float replace)
+{
+ char *s = talloc_asprintf(NULL, "%e", replace);
+ replace_var_str(text, name, s);
+ talloc_free(s);
+}
+
+static void replace_var_char(char **text, const char *name, char replace)
+{
+ char s[2] = { replace, '\0' };
+ replace_var_str(text, name, s);
+}
+
+// Append template to *text. Possibly initialize *text if it's NULL.
+static void append_template(char **text, const char* template)
+{
+ if (!text)
+ *text = talloc_strdup(NULL, template);
+ else
+ *text = talloc_strdup_append(*text, template);
+}
+
+/**
+ * \brief helper function for gen_spline_lookup_tex
+ * \param x subpixel-position ((0,1) range) to calculate weights for
+ * \param dst where to store transformed weights, must provide space for 4 GLfloats
+ *
+ * calculates the weights and stores them after appropriate transformation
+ * for the scaler fragment program.
+ */
+static void store_weights(float x, GLfloat *dst)
+{
+ float w0 = (((-1 * x + 3) * x - 3) * x + 1) / 6;
+ float w1 = (((3 * x - 6) * x + 0) * x + 4) / 6;
+ float w2 = (((-3 * x + 3) * x + 3) * x + 1) / 6;
+ float w3 = (((1 * x + 0) * x + 0) * x + 0) / 6;
+ *dst++ = 1 + x - w1 / (w0 + w1);
+ *dst++ = 1 - x + w3 / (w2 + w3);
+ *dst++ = w0 + w1;
+ *dst++ = 0;
+}
+
+//! to avoid artefacts this should be rather large
+#define LOOKUP_BSPLINE_RES (2 * 1024)
+/**
+ * \brief creates the 1D lookup texture needed for fast higher-order filtering
+ * \param unit texture unit to attach texture to
+ */
+static void gen_spline_lookup_tex(GL *gl, GLenum unit)
+{
+ GLfloat *tex = calloc(4 * LOOKUP_BSPLINE_RES, sizeof(*tex));
+ GLfloat *tp = tex;
+ int i;
+ for (i = 0; i < LOOKUP_BSPLINE_RES; i++) {
+ float x = (float)(i + 0.5) / LOOKUP_BSPLINE_RES;
+ store_weights(x, tp);
+ tp += 4;
+ }
+ store_weights(0, tex);
+ store_weights(1, &tex[4 * (LOOKUP_BSPLINE_RES - 1)]);
+ gl->ActiveTexture(unit);
+ gl->TexImage1D(GL_TEXTURE_1D, 0, GL_RGBA16, LOOKUP_BSPLINE_RES, 0, GL_RGBA,
+ GL_FLOAT, tex);
+ gl->TexParameterf(GL_TEXTURE_1D, GL_TEXTURE_PRIORITY, 1.0);
+ gl->TexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ gl->TexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ gl->TexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ gl->ActiveTexture(GL_TEXTURE0);
+ free(tex);
+}
+
+#define NOISE_RES 2048
+
+/**
+ * \brief creates the 1D lookup texture needed to generate pseudo-random numbers.
+ * \param unit texture unit to attach texture to
+ */
+static void gen_noise_lookup_tex(GL *gl, GLenum unit) {
+ GLfloat *tex = calloc(NOISE_RES, sizeof(*tex));
+ uint32_t lcg = 0x79381c11;
+ int i;
+ for (i = 0; i < NOISE_RES; i++)
+ tex[i] = (double)i / (NOISE_RES - 1);
+ for (i = 0; i < NOISE_RES - 1; i++) {
+ int remain = NOISE_RES - i;
+ int idx = i + (lcg >> 16) % remain;
+ GLfloat tmp = tex[i];
+ tex[i] = tex[idx];
+ tex[idx] = tmp;
+ lcg = lcg * 1664525 + 1013904223;
+ }
+ gl->ActiveTexture(unit);
+ gl->TexImage1D(GL_TEXTURE_1D, 0, 1, NOISE_RES, 0, GL_RED, GL_FLOAT, tex);
+ gl->TexParameterf(GL_TEXTURE_1D, GL_TEXTURE_PRIORITY, 1.0);
+ gl->TexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ gl->TexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ gl->TexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ gl->ActiveTexture(GL_TEXTURE0);
+ free(tex);
+}
+
+#define SAMPLE(dest, coord, texture) \
+ "TEX textemp, " coord ", " texture ", $tex_type;\n" \
+ "MOV " dest ", textemp.r;\n"
+
+static const char bilin_filt_template[] =
+ SAMPLE("yuv.$out_comp","fragment.texcoord[$in_tex]","texture[$in_tex]");
+
+#define BICUB_FILT_MAIN \
+ /* first y-interpolation */ \
+ "ADD coord, fragment.texcoord[$in_tex].xyxy, cdelta.xyxw;\n" \
+ "ADD coord2, fragment.texcoord[$in_tex].xyxy, cdelta.zyzw;\n" \
+ SAMPLE("a.r","coord.xyxy","texture[$in_tex]") \
+ SAMPLE("a.g","coord.zwzw","texture[$in_tex]") \
+ /* second y-interpolation */ \
+ SAMPLE("b.r","coord2.xyxy","texture[$in_tex]") \
+ SAMPLE("b.g","coord2.zwzw","texture[$in_tex]") \
+ "LRP a.b, parmy.b, a.rrrr, a.gggg;\n" \
+ "LRP a.a, parmy.b, b.rrrr, b.gggg;\n" \
+ /* x-interpolation */ \
+ "LRP yuv.$out_comp, parmx.b, a.bbbb, a.aaaa;\n"
+
+static const char bicub_filt_template_2D[] =
+ "MAD coord.xy, fragment.texcoord[$in_tex], {$texw, $texh}, {0.5, 0.5};\n"
+ "TEX parmx, coord.x, texture[$texs], 1D;\n"
+ "MUL cdelta.xz, parmx.rrgg, {-$ptw, 0, $ptw, 0};\n"
+ "TEX parmy, coord.y, texture[$texs], 1D;\n"
+ "MUL cdelta.yw, parmy.rrgg, {0, -$pth, 0, $pth};\n"
+ BICUB_FILT_MAIN;
+
+static const char bicub_filt_template_RECT[] =
+ "ADD coord, fragment.texcoord[$in_tex], {0.5, 0.5};\n"
+ "TEX parmx, coord.x, texture[$texs], 1D;\n"
+ "MUL cdelta.xz, parmx.rrgg, {-1, 0, 1, 0};\n"
+ "TEX parmy, coord.y, texture[$texs], 1D;\n"
+ "MUL cdelta.yw, parmy.rrgg, {0, -1, 0, 1};\n"
+ BICUB_FILT_MAIN;
+
+#define CALCWEIGHTS(t, s) \
+ "MAD "t ", {-0.5, 0.1666, 0.3333, -0.3333}, "s ", {1, 0, -0.5, 0.5};\n" \
+ "MAD "t ", "t ", "s ", {0, 0, -0.5, 0.5};\n" \
+ "MAD "t ", "t ", "s ", {-0.6666, 0, 0.8333, 0.1666};\n" \
+ "RCP a.x, "t ".z;\n" \
+ "RCP a.y, "t ".w;\n" \
+ "MAD "t ".xy, "t ".xyxy, a.xyxy, {1, 1, 0, 0};\n" \
+ "ADD "t ".x, "t ".xxxx, "s ";\n" \
+ "SUB "t ".y, "t ".yyyy, "s ";\n"
+
+static const char bicub_notex_filt_template_2D[] =
+ "MAD coord.xy, fragment.texcoord[$in_tex], {$texw, $texh}, {0.5, 0.5};\n"
+ "FRC coord.xy, coord.xyxy;\n"
+ CALCWEIGHTS("parmx", "coord.xxxx")
+ "MUL cdelta.xz, parmx.rrgg, {-$ptw, 0, $ptw, 0};\n"
+ CALCWEIGHTS("parmy", "coord.yyyy")
+ "MUL cdelta.yw, parmy.rrgg, {0, -$pth, 0, $pth};\n"
+ BICUB_FILT_MAIN;
+
+static const char bicub_notex_filt_template_RECT[] =
+ "ADD coord, fragment.texcoord[$in_tex], {0.5, 0.5};\n"
+ "FRC coord.xy, coord.xyxy;\n"
+ CALCWEIGHTS("parmx", "coord.xxxx")
+ "MUL cdelta.xz, parmx.rrgg, {-1, 0, 1, 0};\n"
+ CALCWEIGHTS("parmy", "coord.yyyy")
+ "MUL cdelta.yw, parmy.rrgg, {0, -1, 0, 1};\n"
+ BICUB_FILT_MAIN;
+
+#define BICUB_X_FILT_MAIN \
+ "ADD coord.xy, fragment.texcoord[$in_tex].xyxy, cdelta.xyxy;\n" \
+ "ADD coord2.xy, fragment.texcoord[$in_tex].xyxy, cdelta.zyzy;\n" \
+ SAMPLE("a.r","coord","texture[$in_tex]") \
+ SAMPLE("b.r","coord2","texture[$in_tex]") \
+ /* x-interpolation */ \
+ "LRP yuv.$out_comp, parmx.b, a.rrrr, b.rrrr;\n"
+
+static const char bicub_x_filt_template_2D[] =
+ "MAD coord.x, fragment.texcoord[$in_tex], {$texw}, {0.5};\n"
+ "TEX parmx, coord, texture[$texs], 1D;\n"
+ "MUL cdelta.xyz, parmx.rrgg, {-$ptw, 0, $ptw};\n"
+ BICUB_X_FILT_MAIN;
+
+static const char bicub_x_filt_template_RECT[] =
+ "ADD coord.x, fragment.texcoord[$in_tex], {0.5};\n"
+ "TEX parmx, coord, texture[$texs], 1D;\n"
+ "MUL cdelta.xyz, parmx.rrgg, {-1, 0, 1};\n"
+ BICUB_X_FILT_MAIN;
+
+static const char unsharp_filt_template[] =
+ "PARAM dcoord$out_comp = {$ptw_05, $pth_05, $ptw_05, -$pth_05};\n"
+ "ADD coord, fragment.texcoord[$in_tex].xyxy, dcoord$out_comp;\n"
+ "SUB coord2, fragment.texcoord[$in_tex].xyxy, dcoord$out_comp;\n"
+ SAMPLE("a.r","fragment.texcoord[$in_tex]","texture[$in_tex]")
+ SAMPLE("b.r","coord.xyxy","texture[$in_tex]")
+ SAMPLE("b.g","coord.zwzw","texture[$in_tex]")
+ "ADD b.r, b.r, b.g;\n"
+ SAMPLE("b.b","coord2.xyxy","texture[$in_tex]")
+ SAMPLE("b.g","coord2.zwzw","texture[$in_tex]")
+ "DP3 b, b, {0.25, 0.25, 0.25};\n"
+ "SUB b.r, a.r, b.r;\n"
+ "MAD textemp.r, b.r, {$strength}, a.r;\n"
+ "MOV yuv.$out_comp, textemp.r;\n";
+
+static const char unsharp_filt_template2[] =
+ "PARAM dcoord$out_comp = {$ptw_12, $pth_12, $ptw_12, -$pth_12};\n"
+ "PARAM dcoord2$out_comp = {$ptw_15, 0, 0, $pth_15};\n"
+ "ADD coord, fragment.texcoord[$in_tex].xyxy, dcoord$out_comp;\n"
+ "SUB coord2, fragment.texcoord[$in_tex].xyxy, dcoord$out_comp;\n"
+ SAMPLE("a.r","fragment.texcoord[$in_tex]","texture[$in_tex]")
+ SAMPLE("b.r","coord.xyxy","texture[$in_tex]")
+ SAMPLE("b.g","coord.zwzw","texture[$in_tex]")
+ "ADD b.r, b.r, b.g;\n"
+ SAMPLE("b.b","coord2.xyxy","texture[$in_tex]")
+ SAMPLE("b.g","coord2.zwzw","texture[$in_tex]")
+ "ADD b.r, b.r, b.b;\n"
+ "ADD b.a, b.r, b.g;\n"
+ "ADD coord, fragment.texcoord[$in_tex].xyxy, dcoord2$out_comp;\n"
+ "SUB coord2, fragment.texcoord[$in_tex].xyxy, dcoord2$out_comp;\n"
+ SAMPLE("b.r","coord.xyxy","texture[$in_tex]")
+ SAMPLE("b.g","coord.zwzw","texture[$in_tex]")
+ "ADD b.r, b.r, b.g;\n"
+ SAMPLE("b.b","coord2.xyxy","texture[$in_tex]")
+ SAMPLE("b.g","coord2.zwzw","texture[$in_tex]")
+ "DP4 b.r, b, {-0.1171875, -0.1171875, -0.1171875, -0.09765625};\n"
+ "MAD b.r, a.r, {0.859375}, b.r;\n"
+ "MAD textemp.r, b.r, {$strength}, a.r;\n"
+ "MOV yuv.$out_comp, textemp.r;\n";
+
+static const char yuv_prog_template[] =
+ "PARAM ycoef = {$cm11, $cm21, $cm31};\n"
+ "PARAM ucoef = {$cm12, $cm22, $cm32};\n"
+ "PARAM vcoef = {$cm13, $cm23, $cm33};\n"
+ "PARAM offsets = {$cm14, $cm24, $cm34};\n"
+ "TEMP res;\n"
+ "MAD res.rgb, yuv.rrrr, ycoef, offsets;\n"
+ "MAD res.rgb, yuv.gggg, ucoef, res;\n"
+ "MAD res.rgb, yuv.bbbb, vcoef, res;\n";
+
+static const char yuv_pow_prog_template[] =
+ "PARAM ycoef = {$cm11, $cm21, $cm31};\n"
+ "PARAM ucoef = {$cm12, $cm22, $cm32};\n"
+ "PARAM vcoef = {$cm13, $cm23, $cm33};\n"
+ "PARAM offsets = {$cm14, $cm24, $cm34};\n"
+ "PARAM gamma = {$gamma_r, $gamma_g, $gamma_b};\n"
+ "TEMP res;\n"
+ "MAD res.rgb, yuv.rrrr, ycoef, offsets;\n"
+ "MAD res.rgb, yuv.gggg, ucoef, res;\n"
+ "MAD_SAT res.rgb, yuv.bbbb, vcoef, res;\n"
+ "POW res.r, res.r, gamma.r;\n"
+ "POW res.g, res.g, gamma.g;\n"
+ "POW res.b, res.b, gamma.b;\n";
+
+static const char yuv_lookup_prog_template[] =
+ "PARAM ycoef = {$cm11, $cm21, $cm31, 0};\n"
+ "PARAM ucoef = {$cm12, $cm22, $cm32, 0};\n"
+ "PARAM vcoef = {$cm13, $cm23, $cm33, 0};\n"
+ "PARAM offsets = {$cm14, $cm24, $cm34, 0.125};\n"
+ "TEMP res;\n"
+ "MAD res, yuv.rrrr, ycoef, offsets;\n"
+ "MAD res.rgb, yuv.gggg, ucoef, res;\n"
+ "MAD res.rgb, yuv.bbbb, vcoef, res;\n"
+ "TEX res.r, res.raaa, texture[$conv_tex0], 2D;\n"
+ "ADD res.a, res.a, 0.25;\n"
+ "TEX res.g, res.gaaa, texture[$conv_tex0], 2D;\n"
+ "ADD res.a, res.a, 0.25;\n"
+ "TEX res.b, res.baaa, texture[$conv_tex0], 2D;\n";
+
+static const char yuv_lookup3d_prog_template[] =
+ "TEMP res;\n"
+ "TEX res, yuv, texture[$conv_tex0], 3D;\n";
+
+static const char noise_filt_template[] =
+ "MUL coord.xy, fragment.texcoord[0], {$noise_sx, $noise_sy};\n"
+ "TEMP rand;\n"
+ "TEX rand.r, coord.x, texture[$noise_filt_tex], 1D;\n"
+ "ADD rand.r, rand.r, coord.y;\n"
+ "TEX rand.r, rand.r, texture[$noise_filt_tex], 1D;\n"
+ "MAD res.rgb, rand.rrrr, {$noise_str, $noise_str, $noise_str}, res;\n";
+
+/**
+ * \brief creates and initializes helper textures needed for scaling texture read
+ * \param scaler scaler type to create texture for
+ * \param texu contains next free texture unit number
+ * \param texs texture unit ids for the scaler are stored in this array
+ */
+static void create_scaler_textures(GL *gl, int scaler, int *texu, char *texs)
+{
+ switch (scaler) {
+ case YUV_SCALER_BILIN:
+ case YUV_SCALER_BICUB_NOTEX:
+ case YUV_SCALER_UNSHARP:
+ case YUV_SCALER_UNSHARP2:
+ break;
+ case YUV_SCALER_BICUB:
+ case YUV_SCALER_BICUB_X:
+ texs[0] = (*texu)++;
+ gen_spline_lookup_tex(gl, GL_TEXTURE0 + texs[0]);
+ texs[0] += '0';
+ break;
+ default:
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] unknown scaler type %i\n", scaler);
+ }
+}
+
+//! resolution of texture for gamma lookup table
+#define LOOKUP_RES 512
+//! resolution for 3D yuv->rgb conversion lookup table
+#define LOOKUP_3DRES 32
+/**
+ * \brief creates and initializes helper textures needed for yuv conversion
+ * \param params struct containing parameters like brightness, gamma, ...
+ * \param texu contains next free texture unit number
+ * \param texs texture unit ids for the conversion are stored in this array
+ */
+static void create_conv_textures(GL *gl, gl_conversion_params_t *params,
+ int *texu, char *texs)
+{
+ unsigned char *lookup_data = NULL;
+ int conv = YUV_CONVERSION(params->type);
+ switch (conv) {
+ case YUV_CONVERSION_FRAGMENT:
+ case YUV_CONVERSION_FRAGMENT_POW:
+ break;
+ case YUV_CONVERSION_FRAGMENT_LOOKUP:
+ texs[0] = (*texu)++;
+ gl->ActiveTexture(GL_TEXTURE0 + texs[0]);
+ lookup_data = malloc(4 * LOOKUP_RES);
+ mp_gen_gamma_map(lookup_data, LOOKUP_RES, params->csp_params.rgamma);
+ mp_gen_gamma_map(&lookup_data[LOOKUP_RES], LOOKUP_RES,
+ params->csp_params.ggamma);
+ mp_gen_gamma_map(&lookup_data[2 * LOOKUP_RES], LOOKUP_RES,
+ params->csp_params.bgamma);
+ glCreateClearTex(gl, GL_TEXTURE_2D, GL_LUMINANCE8, GL_LUMINANCE,
+ GL_UNSIGNED_BYTE, GL_LINEAR, LOOKUP_RES, 4, 0);
+ glUploadTex(gl, GL_TEXTURE_2D, GL_LUMINANCE, GL_UNSIGNED_BYTE,
+ lookup_data, LOOKUP_RES, 0, 0, LOOKUP_RES, 4, 0);
+ gl->ActiveTexture(GL_TEXTURE0);
+ texs[0] += '0';
+ break;
+ case YUV_CONVERSION_FRAGMENT_LOOKUP3D:
+ {
+ int sz = LOOKUP_3DRES + 2; // texture size including borders
+ if (!gl->TexImage3D) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] Missing 3D texture function!\n");
+ break;
+ }
+ texs[0] = (*texu)++;
+ gl->ActiveTexture(GL_TEXTURE0 + texs[0]);
+ lookup_data = malloc(3 * sz * sz * sz);
+ mp_gen_yuv2rgb_map(&params->csp_params, lookup_data, LOOKUP_3DRES);
+ glAdjustAlignment(gl, sz);
+ gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ gl->TexImage3D(GL_TEXTURE_3D, 0, 3, sz, sz, sz, 1,
+ GL_RGB, GL_UNSIGNED_BYTE, lookup_data);
+ gl->TexParameterf(GL_TEXTURE_3D, GL_TEXTURE_PRIORITY, 1.0);
+ gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP);
+ gl->ActiveTexture(GL_TEXTURE0);
+ texs[0] += '0';
+ }
+ break;
+ default:
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] unknown conversion type %i\n", conv);
+ }
+ free(lookup_data);
+}
+
+/**
+ * \brief adds a scaling texture read at the current fragment program position
+ * \param scaler type of scaler to insert
+ * \param prog pointer to fragment program so far
+ * \param texs array containing the texture unit identifiers for this scaler
+ * \param in_tex texture unit the scaler should read from
+ * \param out_comp component of the yuv variable the scaler stores the result in
+ * \param rect if rectangular (pixel) adressing should be used for in_tex
+ * \param texw width of the in_tex texture
+ * \param texh height of the in_tex texture
+ * \param strength strength of filter effect if the scaler does some kind of filtering
+ */
+static void add_scaler(int scaler, char **prog, char *texs,
+ char in_tex, char out_comp, int rect, int texw, int texh,
+ double strength)
+{
+ const char *ttype = rect ? "RECT" : "2D";
+ const float ptw = rect ? 1.0 : 1.0 / texw;
+ const float pth = rect ? 1.0 : 1.0 / texh;
+ switch (scaler) {
+ case YUV_SCALER_BILIN:
+ append_template(prog, bilin_filt_template);
+ break;
+ case YUV_SCALER_BICUB:
+ if (rect)
+ append_template(prog, bicub_filt_template_RECT);
+ else
+ append_template(prog, bicub_filt_template_2D);
+ break;
+ case YUV_SCALER_BICUB_X:
+ if (rect)
+ append_template(prog, bicub_x_filt_template_RECT);
+ else
+ append_template(prog, bicub_x_filt_template_2D);
+ break;
+ case YUV_SCALER_BICUB_NOTEX:
+ if (rect)
+ append_template(prog, bicub_notex_filt_template_RECT);
+ else
+ append_template(prog, bicub_notex_filt_template_2D);
+ break;
+ case YUV_SCALER_UNSHARP:
+ append_template(prog, unsharp_filt_template);
+ break;
+ case YUV_SCALER_UNSHARP2:
+ append_template(prog, unsharp_filt_template2);
+ break;
+ }
+
+ replace_var_char(prog, "texs", texs[0]);
+ replace_var_char(prog, "in_tex", in_tex);
+ replace_var_char(prog, "out_comp", out_comp);
+ replace_var_str(prog, "tex_type", ttype);
+ replace_var_float(prog, "texw", texw);
+ replace_var_float(prog, "texh", texh);
+ replace_var_float(prog, "ptw", ptw);
+ replace_var_float(prog, "pth", pth);
+
+ // this is silly, not sure if that couldn't be in the shader source instead
+ replace_var_float(prog, "ptw_05", ptw * 0.5);
+ replace_var_float(prog, "pth_05", pth * 0.5);
+ replace_var_float(prog, "ptw_15", ptw * 1.5);
+ replace_var_float(prog, "pth_15", pth * 1.5);
+ replace_var_float(prog, "ptw_12", ptw * 1.2);
+ replace_var_float(prog, "pth_12", pth * 1.2);
+
+ replace_var_float(prog, "strength", strength);
+}
+
+static const struct {
+ const char *name;
+ GLenum cur;
+ GLenum max;
+} progstats[] = {
+ {"instructions", 0x88A0, 0x88A1},
+ {"native instructions", 0x88A2, 0x88A3},
+ {"temporaries", 0x88A4, 0x88A5},
+ {"native temporaries", 0x88A6, 0x88A7},
+ {"parameters", 0x88A8, 0x88A9},
+ {"native parameters", 0x88AA, 0x88AB},
+ {"attribs", 0x88AC, 0x88AD},
+ {"native attribs", 0x88AE, 0x88AF},
+ {"ALU instructions", 0x8805, 0x880B},
+ {"TEX instructions", 0x8806, 0x880C},
+ {"TEX indirections", 0x8807, 0x880D},
+ {"native ALU instructions", 0x8808, 0x880E},
+ {"native TEX instructions", 0x8809, 0x880F},
+ {"native TEX indirections", 0x880A, 0x8810},
+ {NULL, 0, 0}
+};
+
+/**
+ * \brief load the specified GPU Program
+ * \param target program target to load into, only GL_FRAGMENT_PROGRAM is tested
+ * \param prog program string
+ * \return 1 on success, 0 otherwise
+ */
+int loadGPUProgram(GL *gl, GLenum target, char *prog)
+{
+ int i;
+ GLint cur = 0, max = 0, err = 0;
+ if (!gl->ProgramString) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] Missing GPU program function\n");
+ return 0;
+ }
+ gl->ProgramString(target, GL_PROGRAM_FORMAT_ASCII, strlen(prog), prog);
+ gl->GetIntegerv(GL_PROGRAM_ERROR_POSITION, &err);
+ if (err != -1) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "[gl] Error compiling fragment program, make sure your card supports\n"
+ "[gl] GL_ARB_fragment_program (use glxinfo to check).\n"
+ "[gl] Error message:\n %s at %.10s\n",
+ gl->GetString(GL_PROGRAM_ERROR_STRING), &prog[err]);
+ return 0;
+ }
+ if (!gl->GetProgramivARB || !mp_msg_test(MSGT_VO, MSGL_DBG2))
+ return 1;
+ mp_msg(MSGT_VO, MSGL_V, "[gl] Program statistics:\n");
+ for (i = 0; progstats[i].name; i++) {
+ gl->GetProgramivARB(target, progstats[i].cur, &cur);
+ gl->GetProgramivARB(target, progstats[i].max, &max);
+ mp_msg(MSGT_VO, MSGL_V, "[gl] %s: %i/%i\n", progstats[i].name, cur,
+ max);
+ }
+ return 1;
+}
+
+#define MAX_PROGSZ (1024 * 1024)
+
+/**
+ * \brief setup a fragment program that will do YUV->RGB conversion
+ * \param parms struct containing parameters like conversion and scaler type,
+ * brightness, ...
+ */
+static void glSetupYUVFragprog(GL *gl, gl_conversion_params_t *params)
+{
+ int type = params->type;
+ int texw = params->texw;
+ int texh = params->texh;
+ int rect = params->target == GL_TEXTURE_RECTANGLE;
+ static const char prog_hdr[] =
+ "!!ARBfp1.0\n"
+ "OPTION ARB_precision_hint_fastest;\n"
+ // all scaler variables must go here so they aren't defined
+ // multiple times when the same scaler is used more than once
+ "TEMP coord, coord2, cdelta, parmx, parmy, a, b, yuv, textemp;\n";
+ char *yuv_prog = NULL;
+ char **prog = &yuv_prog;
+ int cur_texu = 3;
+ char lum_scale_texs[1] = {0};
+ char chrom_scale_texs[1] = {0};
+ char conv_texs[1];
+ char filt_texs[1] = {0};
+ GLint i;
+ // this is the conversion matrix, with y, u, v factors
+ // for red, green, blue and the constant offsets
+ float yuv2rgb[3][4];
+ int noise = params->noise_strength != 0;
+ create_conv_textures(gl, params, &cur_texu, conv_texs);
+ create_scaler_textures(gl, YUV_LUM_SCALER(type), &cur_texu, lum_scale_texs);
+ if (YUV_CHROM_SCALER(type) == YUV_LUM_SCALER(type))
+ memcpy(chrom_scale_texs, lum_scale_texs, sizeof(chrom_scale_texs));
+ else
+ create_scaler_textures(gl, YUV_CHROM_SCALER(type), &cur_texu,
+ chrom_scale_texs);
+
+ if (noise) {
+ gen_noise_lookup_tex(gl, cur_texu);
+ filt_texs[0] = '0' + cur_texu++;
+ }
+
+ gl->GetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &i);
+ if (i < cur_texu)
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "[gl] %i texture units needed for this type of YUV fragment support (found %i)\n",
+ cur_texu, i);
+ if (!gl->ProgramString) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] ProgramString function missing!\n");
+ return;
+ }
+ append_template(prog, prog_hdr);
+ add_scaler(YUV_LUM_SCALER(type), prog, lum_scale_texs,
+ '0', 'r', rect, texw, texh, params->filter_strength);
+ add_scaler(YUV_CHROM_SCALER(type), prog,
+ chrom_scale_texs, '1', 'g', rect, params->chrom_texw,
+ params->chrom_texh, params->filter_strength);
+ add_scaler(YUV_CHROM_SCALER(type), prog,
+ chrom_scale_texs, '2', 'b', rect, params->chrom_texw,
+ params->chrom_texh, params->filter_strength);
+ mp_get_yuv2rgb_coeffs(&params->csp_params, yuv2rgb);
+ switch (YUV_CONVERSION(type)) {
+ case YUV_CONVERSION_FRAGMENT:
+ append_template(prog, yuv_prog_template);
+ break;
+ case YUV_CONVERSION_FRAGMENT_POW:
+ append_template(prog, yuv_pow_prog_template);
+ break;
+ case YUV_CONVERSION_FRAGMENT_LOOKUP:
+ append_template(prog, yuv_lookup_prog_template);
+ break;
+ case YUV_CONVERSION_FRAGMENT_LOOKUP3D:
+ append_template(prog, yuv_lookup3d_prog_template);
+ break;
+ default:
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] unknown conversion type %i\n",
+ YUV_CONVERSION(type));
+ break;
+ }
+ for (int r = 0; r < 3; r++) {
+ for (int c = 0; c < 4; c++) {
+ // "cmRC"
+ char var[] = { 'c', 'm', '1' + r, '1' + c, '\0' };
+ replace_var_float(prog, var, yuv2rgb[r][c]);
+ }
+ }
+ replace_var_float(prog, "gamma_r", (float)1.0 / params->csp_params.rgamma);
+ replace_var_float(prog, "gamma_g", (float)1.0 / params->csp_params.ggamma);
+ replace_var_float(prog, "gamma_b", (float)1.0 / params->csp_params.bgamma);
+ replace_var_char(prog, "conv_tex0", conv_texs[0]);
+
+ if (noise) {
+ // 1.0 strength is suitable for dithering 8 to 6 bit
+ double str = params->noise_strength * (1.0 / 64);
+ double scale_x = (double)NOISE_RES / texw;
+ double scale_y = (double)NOISE_RES / texh;
+ if (rect) {
+ scale_x /= texw;
+ scale_y /= texh;
+ }
+ append_template(prog, noise_filt_template);
+ replace_var_float(prog, "noise_sx", scale_x);
+ replace_var_float(prog, "noise_sy", scale_y);
+ replace_var_char(prog, "noise_filt_tex", filt_texs[0]);
+ replace_var_float(prog, "noise_str", str);
+ }
+
+ append_template(prog, "MOV result.color.rgb, res;\nEND");
+
+ mp_msg(MSGT_VO, MSGL_DBG2, "[gl] generated fragment program:\n%s\n",
+ yuv_prog);
+ loadGPUProgram(gl, GL_FRAGMENT_PROGRAM, yuv_prog);
+ talloc_free(yuv_prog);
+}
+
+/**
+ * \brief detect the best YUV->RGB conversion method available
+ */
+int glAutodetectYUVConversion(GL *gl)
+{
+ const char *extensions = gl->GetString(GL_EXTENSIONS);
+ if (!extensions || !gl->MultiTexCoord2f)
+ return YUV_CONVERSION_NONE;
+ if (strstr(extensions, "GL_ARB_fragment_program"))
+ return YUV_CONVERSION_FRAGMENT;
+ if (strstr(extensions, "GL_ATI_text_fragment_shader"))
+ return YUV_CONVERSION_TEXT_FRAGMENT;
+ if (strstr(extensions, "GL_ATI_fragment_shader"))
+ return YUV_CONVERSION_COMBINERS_ATI;
+ return YUV_CONVERSION_NONE;
+}
+
+/**
+ * \brief setup YUV->RGB conversion
+ * \param parms struct containing parameters like conversion and scaler type,
+ * brightness, ...
+ * \ingroup glconversion
+ */
+void glSetupYUVConversion(GL *gl, gl_conversion_params_t *params)
+{
+ if (params->chrom_texw == 0)
+ params->chrom_texw = 1;
+ if (params->chrom_texh == 0)
+ params->chrom_texh = 1;
+ switch (YUV_CONVERSION(params->type)) {
+ case YUV_CONVERSION_COMBINERS_ATI:
+ glSetupYUVFragmentATI(gl, &params->csp_params, 0);
+ break;
+ case YUV_CONVERSION_TEXT_FRAGMENT:
+ glSetupYUVFragmentATI(gl, &params->csp_params, 1);
+ break;
+ case YUV_CONVERSION_FRAGMENT_LOOKUP:
+ case YUV_CONVERSION_FRAGMENT_LOOKUP3D:
+ case YUV_CONVERSION_FRAGMENT:
+ case YUV_CONVERSION_FRAGMENT_POW:
+ glSetupYUVFragprog(gl, params);
+ break;
+ case YUV_CONVERSION_NONE:
+ break;
+ default:
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] unknown conversion type %i\n",
+ YUV_CONVERSION(params->type));
+ }
+}
+
+/**
+ * \brief enable the specified YUV conversion
+ * \param target texture target for Y, U and V textures (e.g. GL_TEXTURE_2D)
+ * \param type type of YUV conversion
+ * \ingroup glconversion
+ */
+void glEnableYUVConversion(GL *gl, GLenum target, int type)
+{
+ switch (YUV_CONVERSION(type)) {
+ case YUV_CONVERSION_COMBINERS_ATI:
+ gl->ActiveTexture(GL_TEXTURE1);
+ gl->Enable(target);
+ gl->ActiveTexture(GL_TEXTURE2);
+ gl->Enable(target);
+ gl->ActiveTexture(GL_TEXTURE0);
+ gl->Enable(GL_FRAGMENT_SHADER_ATI);
+ break;
+ case YUV_CONVERSION_TEXT_FRAGMENT:
+ gl->ActiveTexture(GL_TEXTURE1);
+ gl->Enable(target);
+ gl->ActiveTexture(GL_TEXTURE2);
+ gl->Enable(target);
+ gl->ActiveTexture(GL_TEXTURE0);
+ gl->Enable(GL_TEXT_FRAGMENT_SHADER_ATI);
+ break;
+ case YUV_CONVERSION_FRAGMENT_LOOKUP3D:
+ case YUV_CONVERSION_FRAGMENT_LOOKUP:
+ case YUV_CONVERSION_FRAGMENT_POW:
+ case YUV_CONVERSION_FRAGMENT:
+ case YUV_CONVERSION_NONE:
+ gl->Enable(GL_FRAGMENT_PROGRAM);
+ break;
+ }
+}
+
+/**
+ * \brief disable the specified YUV conversion
+ * \param target texture target for Y, U and V textures (e.g. GL_TEXTURE_2D)
+ * \param type type of YUV conversion
+ * \ingroup glconversion
+ */
+void glDisableYUVConversion(GL *gl, GLenum target, int type)
+{
+ switch (YUV_CONVERSION(type)) {
+ case YUV_CONVERSION_COMBINERS_ATI:
+ gl->ActiveTexture(GL_TEXTURE1);
+ gl->Disable(target);
+ gl->ActiveTexture(GL_TEXTURE2);
+ gl->Disable(target);
+ gl->ActiveTexture(GL_TEXTURE0);
+ gl->Disable(GL_FRAGMENT_SHADER_ATI);
+ break;
+ case YUV_CONVERSION_TEXT_FRAGMENT:
+ gl->Disable(GL_TEXT_FRAGMENT_SHADER_ATI);
+ // HACK: at least the Mac OS X 10.5 PPC Radeon drivers are broken and
+ // without this disable the texture units while the program is still
+ // running (10.4 PPC seems to work without this though).
+ gl->Flush();
+ gl->ActiveTexture(GL_TEXTURE1);
+ gl->Disable(target);
+ gl->ActiveTexture(GL_TEXTURE2);
+ gl->Disable(target);
+ gl->ActiveTexture(GL_TEXTURE0);
+ break;
+ case YUV_CONVERSION_FRAGMENT_LOOKUP3D:
+ case YUV_CONVERSION_FRAGMENT_LOOKUP:
+ case YUV_CONVERSION_FRAGMENT_POW:
+ case YUV_CONVERSION_FRAGMENT:
+ case YUV_CONVERSION_NONE:
+ gl->Disable(GL_FRAGMENT_PROGRAM);
+ break;
+ }
+}
+
+void glEnable3DLeft(GL *gl, int type)
+{
+ GLint buffer;
+ switch (type) {
+ case GL_3D_RED_CYAN:
+ gl->ColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_FALSE);
+ break;
+ case GL_3D_GREEN_MAGENTA:
+ gl->ColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_FALSE);
+ break;
+ case GL_3D_QUADBUFFER:
+ gl->GetIntegerv(GL_DRAW_BUFFER, &buffer);
+ switch (buffer) {
+ case GL_FRONT:
+ case GL_FRONT_LEFT:
+ case GL_FRONT_RIGHT:
+ buffer = GL_FRONT_LEFT;
+ break;
+ case GL_BACK:
+ case GL_BACK_LEFT:
+ case GL_BACK_RIGHT:
+ buffer = GL_BACK_LEFT;
+ break;
+ }
+ gl->DrawBuffer(buffer);
+ break;
+ }
+}
+
+void glEnable3DRight(GL *gl, int type)
+{
+ GLint buffer;
+ switch (type) {
+ case GL_3D_RED_CYAN:
+ gl->ColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_FALSE);
+ break;
+ case GL_3D_GREEN_MAGENTA:
+ gl->ColorMask(GL_TRUE, GL_FALSE, GL_TRUE, GL_FALSE);
+ break;
+ case GL_3D_QUADBUFFER:
+ gl->GetIntegerv(GL_DRAW_BUFFER, &buffer);
+ switch (buffer) {
+ case GL_FRONT:
+ case GL_FRONT_LEFT:
+ case GL_FRONT_RIGHT:
+ buffer = GL_FRONT_RIGHT;
+ break;
+ case GL_BACK:
+ case GL_BACK_LEFT:
+ case GL_BACK_RIGHT:
+ buffer = GL_BACK_RIGHT;
+ break;
+ }
+ gl->DrawBuffer(buffer);
+ break;
+ }
+}
+
+void glDisable3D(GL *gl, int type)
+{
+ GLint buffer;
+ switch (type) {
+ case GL_3D_RED_CYAN:
+ case GL_3D_GREEN_MAGENTA:
+ gl->ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ break;
+ case GL_3D_QUADBUFFER:
+ gl->DrawBuffer(GL_BACK);
+ gl->GetIntegerv(GL_DRAW_BUFFER, &buffer);
+ switch (buffer) {
+ case GL_FRONT:
+ case GL_FRONT_LEFT:
+ case GL_FRONT_RIGHT:
+ buffer = GL_FRONT;
+ break;
+ case GL_BACK:
+ case GL_BACK_LEFT:
+ case GL_BACK_RIGHT:
+ buffer = GL_BACK;
+ break;
+ }
+ gl->DrawBuffer(buffer);
+ break;
+ }
+}
+
+/**
+ * \brief draw a texture part at given 2D coordinates
+ * \param x screen top coordinate
+ * \param y screen left coordinate
+ * \param w screen width coordinate
+ * \param h screen height coordinate
+ * \param tx texture top coordinate in pixels
+ * \param ty texture left coordinate in pixels
+ * \param tw texture part width in pixels
+ * \param th texture part height in pixels
+ * \param sx width of texture in pixels
+ * \param sy height of texture in pixels
+ * \param rect_tex whether this texture uses texture_rectangle extension
+ * \param is_yv12 if != 0, also draw the textures from units 1 and 2,
+ * bits 8 - 15 and 16 - 23 specify the x and y scaling of those textures
+ * \param flip flip the texture upside down
+ * \ingroup gltexture
+ */
+void glDrawTex(GL *gl, GLfloat x, GLfloat y, GLfloat w, GLfloat h,
+ GLfloat tx, GLfloat ty, GLfloat tw, GLfloat th,
+ int sx, int sy, int rect_tex, int is_yv12, int flip)
+{
+ int chroma_x_shift = (is_yv12 >> 8) & 31;
+ int chroma_y_shift = (is_yv12 >> 16) & 31;
+ GLfloat xscale = 1 << chroma_x_shift;
+ GLfloat yscale = 1 << chroma_y_shift;
+ GLfloat tx2 = tx / xscale, ty2 = ty / yscale, tw2 = tw / xscale, th2 = th / yscale;
+ if (!rect_tex) {
+ tx /= sx;
+ ty /= sy;
+ tw /= sx;
+ th /= sy;
+ tx2 = tx, ty2 = ty, tw2 = tw, th2 = th;
+ }
+ if (flip) {
+ y += h;
+ h = -h;
+ }
+ gl->Begin(GL_QUADS);
+ gl->TexCoord2f(tx, ty);
+ if (is_yv12) {
+ gl->MultiTexCoord2f(GL_TEXTURE1, tx2, ty2);
+ gl->MultiTexCoord2f(GL_TEXTURE2, tx2, ty2);
+ }
+ gl->Vertex2f(x, y);
+ gl->TexCoord2f(tx, ty + th);
+ if (is_yv12) {
+ gl->MultiTexCoord2f(GL_TEXTURE1, tx2, ty2 + th2);
+ gl->MultiTexCoord2f(GL_TEXTURE2, tx2, ty2 + th2);
+ }
+ gl->Vertex2f(x, y + h);
+ gl->TexCoord2f(tx + tw, ty + th);
+ if (is_yv12) {
+ gl->MultiTexCoord2f(GL_TEXTURE1, tx2 + tw2, ty2 + th2);
+ gl->MultiTexCoord2f(GL_TEXTURE2, tx2 + tw2, ty2 + th2);
+ }
+ gl->Vertex2f(x + w, y + h);
+ gl->TexCoord2f(tx + tw, ty);
+ if (is_yv12) {
+ gl->MultiTexCoord2f(GL_TEXTURE1, tx2 + tw2, ty2);
+ gl->MultiTexCoord2f(GL_TEXTURE2, tx2 + tw2, ty2);
+ }
+ gl->Vertex2f(x + w, y);
+ gl->End();
+}
+
+mp_image_t *glGetWindowScreenshot(GL *gl)
+{
+ GLint vp[4]; //x, y, w, h
+ gl->GetIntegerv(GL_VIEWPORT, vp);
+ mp_image_t *image = alloc_mpi(vp[2], vp[3], IMGFMT_RGB24);
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+ gl->PixelStorei(GL_PACK_ALIGNMENT, 0);
+ gl->PixelStorei(GL_PACK_ROW_LENGTH, 0);
+ gl->ReadBuffer(GL_FRONT);
+ //flip image while reading
+ for (int y = 0; y < vp[3]; y++) {
+ gl->ReadPixels(vp[0], vp[1] + vp[3] - y - 1, vp[2], 1,
+ GL_RGB, GL_UNSIGNED_BYTE,
+ image->planes[0] + y * image->stride[0]);
+ }
+ return image;
+}
+
+#ifdef CONFIG_GL_COCOA
+#include "cocoa_common.h"
+
+static bool create_window_cocoa(struct MPGLContext *ctx, uint32_t d_width,
+ uint32_t d_height, uint32_t flags, bool gl3)
+{
+ int rv = vo_cocoa_create_window(ctx->vo, d_width, d_height, flags, gl3);
+ if (rv != 0)
+ return false;
+
+ getFunctions(ctx->gl, (void *)vo_cocoa_glgetaddr, NULL, gl3);
+
+ if (gl3) {
+ ctx->depth_r = vo_cocoa_cgl_color_size(ctx->vo);
+ ctx->depth_g = vo_cocoa_cgl_color_size(ctx->vo);
+ ctx->depth_b = vo_cocoa_cgl_color_size(ctx->vo);
+ }
+
+ if (!ctx->gl->SwapInterval)
+ ctx->gl->SwapInterval = vo_cocoa_swap_interval;
+
+ return true;
+}
+
+static bool create_window_cocoa_old(struct MPGLContext *ctx, uint32_t d_width,
+ uint32_t d_height, uint32_t flags)
+{
+ return create_window_cocoa(ctx, d_width, d_height, flags, false);
+}
+
+static bool create_window_cocoa_gl3(struct MPGLContext *ctx, uint32_t d_width,
+ uint32_t d_height, uint32_t flags)
+{
+ return create_window_cocoa(ctx, d_width, d_height, flags, true);
+}
+
+static void releaseGlContext_cocoa(MPGLContext *ctx)
+{
+}
+
+static void swapGlBuffers_cocoa(MPGLContext *ctx)
+{
+ vo_cocoa_swap_buffers(ctx->vo);
+}
+#endif
+
+#ifdef CONFIG_GL_WIN32
+#include <windows.h>
+#include "w32_common.h"
+
+struct w32_context {
+ HGLRC context;
+};
+
+static void *w32gpa(const GLubyte *procName)
+{
+ HMODULE oglmod;
+ void *res = wglGetProcAddress(procName);
+ if (res)
+ return res;
+ oglmod = GetModuleHandle("opengl32.dll");
+ return GetProcAddress(oglmod, procName);
+}
+
+static bool create_window_w32_old(struct MPGLContext *ctx, uint32_t d_width,
+ uint32_t d_height, uint32_t flags)
+{
+ GL *gl = ctx->gl;
+
+ if (!vo_w32_config(ctx->vo, d_width, d_height, flags))
+ return false;
+
+ struct w32_context *w32_ctx = ctx->priv;
+ HGLRC *context = &w32_ctx->context;
+
+ if (*context) {
+ gl->Finish(); // supposedly to prevent flickering
+ return true;
+ }
+
+ HWND win = ctx->vo->w32->window;
+ HDC windc = vo_w32_get_dc(ctx->vo, win);
+ bool res = false;
+
+ HGLRC new_context = wglCreateContext(windc);
+ if (!new_context) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not create GL context!\n");
+ goto out;
+ }
+
+ if (!wglMakeCurrent(windc, new_context)) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not set GL context!\n");
+ wglDeleteContext(new_context);
+ goto out;
+ }
+
+ *context = new_context;
+
+ getFunctions(ctx->gl, w32gpa, NULL, false);
+ res = true;
+
+out:
+ vo_w32_release_dc(ctx->vo, win, windc);
+ return res;
+}
+
+static bool create_window_w32_gl3(struct MPGLContext *ctx, uint32_t d_width,
+ uint32_t d_height, uint32_t flags)
+{
+ if (!vo_w32_config(ctx->vo, d_width, d_height, flags))
+ return false;
+
+ struct w32_context *w32_ctx = ctx->priv;
+ HGLRC *context = &w32_ctx->context;
+
+ if (*context) // reuse existing context
+ return true; // not reusing it breaks gl3!
+
+ HWND win = ctx->vo->w32->window;
+ HDC windc = vo_w32_get_dc(ctx->vo, win);
+ HGLRC new_context = 0;
+
+ new_context = wglCreateContext(windc);
+ if (!new_context) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not create GL context!\n");
+ return false;
+ }
+
+ // set context
+ if (!wglMakeCurrent(windc, new_context)) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not set GL context!\n");
+ goto out;
+ }
+
+ const char *(GLAPIENTRY *wglGetExtensionsStringARB)(HDC hdc)
+ = w32gpa((const GLubyte*)"wglGetExtensionsStringARB");
+
+ if (!wglGetExtensionsStringARB)
+ goto unsupported;
+
+ const char *wgl_exts = wglGetExtensionsStringARB(windc);
+ if (!strstr(wgl_exts, "WGL_ARB_create_context"))
+ goto unsupported;
+
+ HGLRC (GLAPIENTRY *wglCreateContextAttribsARB)(HDC hDC, HGLRC hShareContext,
+ const int *attribList)
+ = w32gpa((const GLubyte*)"wglCreateContextAttribsARB");
+
+ if (!wglCreateContextAttribsARB)
+ goto unsupported;
+
+ int gl_version = ctx->requested_gl_version;
+ int attribs[] = {
+ WGL_CONTEXT_MAJOR_VERSION_ARB, MPGL_VER_GET_MAJOR(gl_version),
+ WGL_CONTEXT_MINOR_VERSION_ARB, MPGL_VER_GET_MINOR(gl_version),
+ WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
+ WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
+ 0
+ };
+
+ *context = wglCreateContextAttribsARB(windc, 0, attribs);
+ if (! *context) {
+ // NVidia, instead of ignoring WGL_CONTEXT_FLAGS_ARB, will error out if
+ // it's present on pre-3.2 contexts.
+ // Remove it from attribs and retry the context creation.
+ attribs[6] = attribs[7] = 0;
+ *context = wglCreateContextAttribsARB(windc, 0, attribs);
+ }
+ if (! *context) {
+ int err = GetLastError();
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not create an OpenGL 3.x"
+ " context: error 0x%x\n", err);
+ goto out;
+ }
+
+ wglMakeCurrent(NULL, NULL);
+ wglDeleteContext(new_context);
+
+ if (!wglMakeCurrent(windc, *context)) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not set GL3 context!\n");
+ wglDeleteContext(*context);
+ return false;
+ }
+
+ /* update function pointers */
+ getFunctions(ctx->gl, w32gpa, NULL, true);
+
+ int pfmt = GetPixelFormat(windc);
+ PIXELFORMATDESCRIPTOR pfd;
+ if (DescribePixelFormat(windc, pfmt, sizeof(PIXELFORMATDESCRIPTOR), &pfd)) {
+ ctx->depth_r = pfd.cRedBits;
+ ctx->depth_g = pfd.cGreenBits;
+ ctx->depth_b = pfd.cBlueBits;
+ }
+
+ return true;
+
+unsupported:
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] The current OpenGL implementation does"
+ " not support OpenGL 3.x \n");
+out:
+ wglDeleteContext(new_context);
+ return false;
+}
+
+static void releaseGlContext_w32(MPGLContext *ctx)
+{
+ struct w32_context *w32_ctx = ctx->priv;
+ HGLRC *context = &w32_ctx->context;
+ if (*context) {
+ wglMakeCurrent(0, 0);
+ wglDeleteContext(*context);
+ }
+ *context = 0;
+}
+
+static void swapGlBuffers_w32(MPGLContext *ctx)
+{
+ HDC vo_hdc = vo_w32_get_dc(ctx->vo, ctx->vo->w32->window);
+ SwapBuffers(vo_hdc);
+ vo_w32_release_dc(ctx->vo, ctx->vo->w32->window, vo_hdc);
+}
+#endif
+
+#ifdef CONFIG_GL_X11
+#include <X11/Xlib.h>
+#include <GL/glx.h>
+#include "x11_common.h"
+
+struct glx_context {
+ XVisualInfo *vinfo;
+ GLXContext context;
+ GLXFBConfig fbc;
+};
+
+// The GL3/FBC initialization code roughly follows/copies from:
+// http://www.opengl.org/wiki/Tutorial:_OpenGL_3.0_Context_Creation_(GLX)
+// but also uses some of the old code.
+
+static GLXFBConfig select_fb_config(struct vo *vo, const int *attribs)
+{
+ int fbcount;
+ GLXFBConfig *fbc = glXChooseFBConfig(vo->x11->display, vo->x11->screen,
+ attribs, &fbcount);
+ if (!fbc)
+ return NULL;
+
+ // The list in fbc is sorted (so that the first element is the best).
+ GLXFBConfig fbconfig = fbc[0];
+
+ XFree(fbc);
+
+ return fbconfig;
+}
+
+static bool create_glx_window(struct MPGLContext *ctx, uint32_t d_width,
+ uint32_t d_height, uint32_t flags)
+{
+ struct vo *vo = ctx->vo;
+ struct glx_context *glx_ctx = ctx->priv;
+
+ if (glx_ctx->context) {
+ // GL context and window already exist.
+ // Only update window geometry etc.
+ Colormap colormap = XCreateColormap(vo->x11->display, vo->x11->rootwin,
+ glx_ctx->vinfo->visual, AllocNone);
+ vo_x11_create_vo_window(vo, glx_ctx->vinfo, vo->dx, vo->dy, d_width,
+ d_height, flags, colormap, "gl");
+ XFreeColormap(vo->x11->display, colormap);
+ return true;
+ }
+
+ int glx_major, glx_minor;
+
+ // FBConfigs were added in GLX version 1.3.
+ if (!glXQueryVersion(vo->x11->display, &glx_major, &glx_minor) ||
+ (MPGL_VER(glx_major, glx_minor) < MPGL_VER(1, 3)))
+ {
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] GLX version older than 1.3.\n");
+ return false;
+ }
+
+ const int glx_attribs_stereo_value_idx = 1; // index of GLX_STEREO + 1
+ int glx_attribs[] = {
+ GLX_STEREO, False,
+ GLX_X_RENDERABLE, True,
+ GLX_RED_SIZE, 1,
+ GLX_GREEN_SIZE, 1,
+ GLX_BLUE_SIZE, 1,
+ GLX_DOUBLEBUFFER, True,
+ None
+ };
+ GLXFBConfig fbc = NULL;
+ if (flags & VOFLAG_STEREO) {
+ glx_attribs[glx_attribs_stereo_value_idx] = True;
+ fbc = select_fb_config(vo, glx_attribs);
+ if (!fbc) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] Could not find a stereo visual,"
+ " 3D will probably not work!\n");
+ glx_attribs[glx_attribs_stereo_value_idx] = False;
+ }
+ }
+ if (!fbc)
+ fbc = select_fb_config(vo, glx_attribs);
+ if (!fbc) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] no GLX support present\n");
+ return false;
+ }
+
+ glx_ctx->fbc = fbc;
+ glx_ctx->vinfo = glXGetVisualFromFBConfig(vo->x11->display, fbc);
+
+ mp_msg(MSGT_VO, MSGL_V, "[gl] GLX chose visual with ID 0x%x\n",
+ (int)glx_ctx->vinfo->visualid);
+
+ glXGetFBConfigAttrib(vo->x11->display, fbc, GLX_RED_SIZE, &ctx->depth_r);
+ glXGetFBConfigAttrib(vo->x11->display, fbc, GLX_GREEN_SIZE, &ctx->depth_g);
+ glXGetFBConfigAttrib(vo->x11->display, fbc, GLX_BLUE_SIZE, &ctx->depth_b);
+
+ Colormap colormap = XCreateColormap(vo->x11->display, vo->x11->rootwin,
+ glx_ctx->vinfo->visual, AllocNone);
+ vo_x11_create_vo_window(vo, glx_ctx->vinfo, vo->dx, vo->dy, d_width,
+ d_height, flags, colormap, "gl");
+ XFreeColormap(vo->x11->display, colormap);
+
+ return true;
+}
+
+static bool create_window_x11_old(struct MPGLContext *ctx, uint32_t d_width,
+ uint32_t d_height, uint32_t flags)
+{
+ struct glx_context *glx_ctx = ctx->priv;
+ Display *display = ctx->vo->x11->display;
+ struct vo *vo = ctx->vo;
+ GL *gl = ctx->gl;
+
+ if (!create_glx_window(ctx, d_width, d_height, flags))
+ return false;
+
+ if (glx_ctx->context)
+ return true;
+
+ GLXContext new_context = glXCreateContext(display, glx_ctx->vinfo, NULL,
+ True);
+ if (!new_context) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not create GLX context!\n");
+ return false;
+ }
+
+ if (!glXMakeCurrent(display, ctx->vo->x11->window, new_context)) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not set GLX context!\n");
+ glXDestroyContext(display, new_context);
+ return false;
+ }
+
+ void *(*getProcAddress)(const GLubyte *);
+ getProcAddress = getdladdr("glXGetProcAddress");
+ if (!getProcAddress)
+ getProcAddress = getdladdr("glXGetProcAddressARB");
+
+ const char *glxstr = "";
+ const char *(*glXExtStr)(Display *, int)
+ = getdladdr("glXQueryExtensionsString");
+ if (glXExtStr)
+ glxstr = glXExtStr(display, ctx->vo->x11->screen);
+
+ getFunctions(gl, getProcAddress, glxstr, false);
+ if (!gl->GenPrograms && gl->GetString &&
+ gl->version < MPGL_VER(3, 0) &&
+ getProcAddress &&
+ strstr(gl->GetString(GL_EXTENSIONS), "GL_ARB_vertex_program"))
+ {
+ mp_msg(MSGT_VO, MSGL_WARN,
+ "Broken glXGetProcAddress detected, trying workaround\n");
+ getFunctions(gl, NULL, glxstr, false);
+ }
+
+ glx_ctx->context = new_context;
+
+ if (!glXIsDirect(vo->x11->display, new_context))
+ ctx->gl->mpgl_caps &= ~MPGL_CAP_NO_SW;
+
+ return true;
+}
+
+typedef GLXContext (*glXCreateContextAttribsARBProc)
+ (Display*, GLXFBConfig, GLXContext, Bool, const int*);
+
+static bool create_window_x11_gl3(struct MPGLContext *ctx, uint32_t d_width,
+ uint32_t d_height, uint32_t flags)
+{
+ struct glx_context *glx_ctx = ctx->priv;
+ struct vo *vo = ctx->vo;
+
+ if (!create_glx_window(ctx, d_width, d_height, flags))
+ return false;
+
+ if (glx_ctx->context)
+ return true;
+
+ glXCreateContextAttribsARBProc glXCreateContextAttribsARB =
+ (glXCreateContextAttribsARBProc)
+ glXGetProcAddressARB((const GLubyte *)"glXCreateContextAttribsARB");
+
+ const char *glxstr = "";
+ const char *(*glXExtStr)(Display *, int)
+ = getdladdr("glXQueryExtensionsString");
+ if (glXExtStr)
+ glxstr = glXExtStr(vo->x11->display, vo->x11->screen);
+ bool have_ctx_ext = glxstr && !!strstr(glxstr, "GLX_ARB_create_context");
+
+ if (!(have_ctx_ext && glXCreateContextAttribsARB)) {
+ return false;
+ }
+
+ int gl_version = ctx->requested_gl_version;
+ int context_attribs[] = {
+ GLX_CONTEXT_MAJOR_VERSION_ARB, MPGL_VER_GET_MAJOR(gl_version),
+ GLX_CONTEXT_MINOR_VERSION_ARB, MPGL_VER_GET_MINOR(gl_version),
+ GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
+ GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB
+ | (flags & VOFLAG_GL_DEBUG ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
+ None
+ };
+ GLXContext context = glXCreateContextAttribsARB(vo->x11->display,
+ glx_ctx->fbc, 0, True,
+ context_attribs);
+ if (!context) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not create GLX context!\n");
+ return false;
+ }
+
+ // set context
+ if (!glXMakeCurrent(vo->x11->display, vo->x11->window, context)) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not set GLX context!\n");
+ glXDestroyContext(vo->x11->display, context);
+ return false;
+ }
+
+ glx_ctx->context = context;
+
+ getFunctions(ctx->gl, (void *)glXGetProcAddress, glxstr, true);
+
+ if (!glXIsDirect(vo->x11->display, context))
+ ctx->gl->mpgl_caps &= ~MPGL_CAP_NO_SW;
+
+ return true;
+}
+
+/**
+ * \brief free the VisualInfo and GLXContext of an OpenGL context.
+ * \ingroup glcontext
+ */
+static void releaseGlContext_x11(MPGLContext *ctx)
+{
+ struct glx_context *glx_ctx = ctx->priv;
+ XVisualInfo **vinfo = &glx_ctx->vinfo;
+ GLXContext *context = &glx_ctx->context;
+ Display *display = ctx->vo->x11->display;
+ GL *gl = ctx->gl;
+ if (*vinfo)
+ XFree(*vinfo);
+ *vinfo = NULL;
+ if (*context) {
+ if (gl->Finish)
+ gl->Finish();
+ glXMakeCurrent(display, None, NULL);
+ glXDestroyContext(display, *context);
+ }
+ *context = 0;
+}
+
+static void swapGlBuffers_x11(MPGLContext *ctx)
+{
+ glXSwapBuffers(ctx->vo->x11->display, ctx->vo->x11->window);
+}
+#endif
+
+
+struct backend {
+ const char *name;
+ enum MPGLType type;
+};
+
+static struct backend backends[] = {
+ {"auto", GLTYPE_AUTO},
+ {"cocoa", GLTYPE_COCOA},
+ {"win", GLTYPE_W32},
+ {"x11", GLTYPE_X11},
+ // mplayer-svn aliases (note that mplayer-svn couples these with the numeric
+ // values of the internal GLTYPE_* constants)
+ {"-1", GLTYPE_AUTO},
+ { "0", GLTYPE_W32},
+ { "1", GLTYPE_X11},
+
+ {0}
+};
+
+int mpgl_find_backend(const char *name)
+{
+ for (const struct backend *entry = backends; entry->name; entry++) {
+ if (strcmp(entry->name, name) == 0)
+ return entry->type;
+ }
+ return -1;
+}
+
+MPGLContext *mpgl_init(enum MPGLType type, struct vo *vo)
+{
+ MPGLContext *ctx;
+ if (type == GLTYPE_AUTO) {
+ ctx = mpgl_init(GLTYPE_COCOA, vo);
+ if (ctx)
+ return ctx;
+ ctx = mpgl_init(GLTYPE_W32, vo);
+ if (ctx)
+ return ctx;
+ return mpgl_init(GLTYPE_X11, vo);
+ }
+ ctx = talloc_zero(NULL, MPGLContext);
+ *ctx = (MPGLContext) {
+ .gl = talloc_zero(ctx, GL),
+ .type = type,
+ .vo = vo,
+ .requested_gl_version = MPGL_VER(3, 0),
+ .vo_init_ok = true,
+ };
+ switch (ctx->type) {
+#ifdef CONFIG_GL_COCOA
+ case GLTYPE_COCOA:
+ ctx->create_window_old = create_window_cocoa_old;
+ ctx->create_window_gl3 = create_window_cocoa_gl3;
+ ctx->releaseGlContext = releaseGlContext_cocoa;
+ ctx->swapGlBuffers = swapGlBuffers_cocoa;
+ ctx->check_events = vo_cocoa_check_events;
+ ctx->update_xinerama_info = vo_cocoa_update_xinerama_info;
+ ctx->fullscreen = vo_cocoa_fullscreen;
+ ctx->ontop = vo_cocoa_ontop;
+ ctx->vo_init = vo_cocoa_init;
+ ctx->pause = vo_cocoa_pause;
+ ctx->resume = vo_cocoa_resume;
+ ctx->vo_uninit = vo_cocoa_uninit;
+ break;
+#endif
+#ifdef CONFIG_GL_WIN32
+ case GLTYPE_W32:
+ ctx->priv = talloc_zero(ctx, struct w32_context);
+ ctx->create_window_old = create_window_w32_old;
+ ctx->create_window_gl3 = create_window_w32_gl3;
+ ctx->releaseGlContext = releaseGlContext_w32;
+ ctx->swapGlBuffers = swapGlBuffers_w32;
+ ctx->update_xinerama_info = w32_update_xinerama_info;
+ ctx->border = vo_w32_border;
+ ctx->check_events = vo_w32_check_events;
+ ctx->fullscreen = vo_w32_fullscreen;
+ ctx->ontop = vo_w32_ontop;
+ ctx->vo_init = vo_w32_init;
+ ctx->vo_uninit = vo_w32_uninit;
+ break;
+#endif
+#ifdef CONFIG_GL_X11
+ case GLTYPE_X11:
+ ctx->priv = talloc_zero(ctx, struct glx_context);
+ ctx->create_window_old = create_window_x11_old;
+ ctx->create_window_gl3 = create_window_x11_gl3;
+ ctx->releaseGlContext = releaseGlContext_x11;
+ ctx->swapGlBuffers = swapGlBuffers_x11;
+ ctx->update_xinerama_info = update_xinerama_info;
+ ctx->border = vo_x11_border;
+ ctx->check_events = vo_x11_check_events;
+ ctx->fullscreen = vo_x11_fullscreen;
+ ctx->ontop = vo_x11_ontop;
+ ctx->vo_init = vo_init;
+ ctx->vo_uninit = vo_x11_uninit;
+ break;
+#endif
+ }
+ if (ctx->vo_init && ctx->vo_init(vo))
+ return ctx;
+ talloc_free(ctx);
+ return NULL;
+}
+
+bool mpgl_destroy_window(struct MPGLContext *ctx)
+{
+ ctx->releaseGlContext(ctx);
+ *ctx->gl = (GL) {0};
+ // This is a caveat. At least on X11, this will recreate the X display
+ // connection. Also, if vo_init() fails, unspecified things will happen.
+ ctx->vo_uninit(ctx->vo);
+ ctx->vo_init_ok = ctx->vo_init(ctx->vo);
+ return ctx->vo_init_ok;
+}
+
+static bool create_window(struct MPGLContext *ctx, int gl_caps,
+ bool (*create)(struct MPGLContext *, uint32_t,
+ uint32_t, uint32_t),
+ uint32_t d_width, uint32_t d_height, uint32_t flags)
+{
+ if (!create || !ctx->vo_init_ok)
+ return false;
+ if (create(ctx, d_width, d_height, flags)) {
+ int missing = (ctx->gl->mpgl_caps & gl_caps) ^ gl_caps;
+ if (!missing) {
+ ctx->selected_create_window = create;
+ return true;
+ }
+ mp_msg(MSGT_VO, MSGL_WARN, "[gl] Missing OpenGL features:");
+ list_features(missing, MSGL_WARN, false);
+ if (missing & MPGL_CAP_NO_SW) {
+ mp_msg(MSGT_VO, MSGL_WARN, "[gl] Rejecting suspected software "
+ "OpenGL renderer.\n");
+ }
+ }
+ // If we tried to create a GL 3 context, and we're going to create a legacy
+ // context after this, the window should be recreated at least on X11.
+ mpgl_destroy_window(ctx);
+ return false;
+}
+
+bool mpgl_create_window(struct MPGLContext *ctx, int gl_caps, uint32_t d_width,
+ uint32_t d_height, uint32_t flags)
+{
+ assert(ctx->vo_init_ok);
+ if (ctx->selected_create_window)
+ return ctx->selected_create_window(ctx, d_width, d_height, flags);
+
+ bool allow_gl3 = !(gl_caps & MPGL_CAP_GL_LEGACY);
+ bool allow_legacy = !(gl_caps & MPGL_CAP_GL3);
+ gl_caps |= MPGL_CAP_GL;
+
+ if (allow_gl3 && create_window(ctx, gl_caps, ctx->create_window_gl3,
+ d_width, d_height, flags))
+ return true;
+
+ if (allow_legacy && create_window(ctx, gl_caps, ctx->create_window_old,
+ d_width, d_height, flags))
+ return true;
+
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] OpenGL context creation failed!\n");
+ return false;
+}
+
+void mpgl_uninit(MPGLContext *ctx)
+{
+ if (!ctx)
+ return;
+ if (ctx->vo_init_ok) {
+ ctx->releaseGlContext(ctx);
+ ctx->vo_uninit(ctx->vo);
+ }
+ talloc_free(ctx);
+}
+
+void mp_log_source(int mod, int lev, const char *src)
+{
+ int line = 1;
+ if (!src)
+ return;
+ while (*src) {
+ const char *end = strchr(src, '\n');
+ const char *next = end + 1;
+ if (!end)
+ next = end = src + strlen(src);
+ mp_msg(mod, lev, "[%3d] %.*s\n", line, (int)(end - src), src);
+ line++;
+ src = next;
+ }
+}
diff --git a/video/out/gl_common.h b/video/out/gl_common.h
new file mode 100644
index 0000000000..9816566097
--- /dev/null
+++ b/video/out/gl_common.h
@@ -0,0 +1,396 @@
+/*
+ * 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_GL_COMMON_H
+#define MPLAYER_GL_COMMON_H
+
+#include <stdio.h>
+#include <stdint.h>
+
+#include "config.h"
+#include "mp_msg.h"
+
+#include "video_out.h"
+#include "csputils.h"
+
+#include "libmpcodecs/mp_image.h"
+
+#if defined(CONFIG_GL_COCOA) && !defined(CONFIG_GL_X11)
+#ifdef GL_VERSION_3_0
+#include <OpenGL/gl3.h>
+#else
+#include <OpenGL/gl.h>
+#endif
+#include <OpenGL/glext.h>
+#else
+#include <GL/gl.h>
+#include <GL/glext.h>
+#endif
+
+#include "libvo/gl_header_fixes.h"
+
+struct GL;
+typedef struct GL GL;
+
+void glAdjustAlignment(GL *gl, int stride);
+
+int glFindFormat(uint32_t format, int have_texture_rg, int *bpp,
+ GLint *gl_texfmt, GLenum *gl_format, GLenum *gl_type);
+int glFmt2bpp(GLenum format, GLenum type);
+void glCreateClearTex(GL *gl, GLenum target, GLenum fmt, GLenum format,
+ GLenum type, GLint filter, int w, int h,
+ unsigned char val);
+int glCreatePPMTex(GL *gl, GLenum target, GLenum fmt, GLint filter,
+ FILE *f, int *width, int *height, int *maxval);
+void glUploadTex(GL *gl, GLenum target, GLenum format, GLenum type,
+ const void *dataptr, int stride,
+ int x, int y, int w, int h, int slice);
+void glClearTex(GL *gl, GLenum target, GLenum format, GLenum type,
+ int x, int y, int w, int h, uint8_t val, void **scratch);
+void glDownloadTex(GL *gl, GLenum target, GLenum format, GLenum type,
+ void *dataptr, int stride);
+void glDrawTex(GL *gl, GLfloat x, GLfloat y, GLfloat w, GLfloat h,
+ GLfloat tx, GLfloat ty, GLfloat tw, GLfloat th,
+ int sx, int sy, int rect_tex, int is_yv12, int flip);
+int loadGPUProgram(GL *gl, GLenum target, char *prog);
+void glCheckError(GL *gl, const char *info);
+mp_image_t *glGetWindowScreenshot(GL *gl);
+
+/** \addtogroup glconversion
+ * \{ */
+//! do not use YUV conversion, this should always stay 0
+#define YUV_CONVERSION_NONE 0
+//! use nVidia specific register combiners for YUV conversion
+//! implementation has been removed
+#define YUV_CONVERSION_COMBINERS 1
+//! use a fragment program for YUV conversion
+#define YUV_CONVERSION_FRAGMENT 2
+//! use a fragment program for YUV conversion with gamma using POW
+#define YUV_CONVERSION_FRAGMENT_POW 3
+//! use a fragment program with additional table lookup for YUV conversion
+#define YUV_CONVERSION_FRAGMENT_LOOKUP 4
+//! use ATI specific register combiners ("fragment program")
+#define YUV_CONVERSION_COMBINERS_ATI 5
+//! use a fragment program with 3D table lookup for YUV conversion
+#define YUV_CONVERSION_FRAGMENT_LOOKUP3D 6
+//! use ATI specific "text" register combiners ("fragment program")
+#define YUV_CONVERSION_TEXT_FRAGMENT 7
+//! use normal bilinear scaling for textures
+#define YUV_SCALER_BILIN 0
+//! use higher quality bicubic scaling for textures
+#define YUV_SCALER_BICUB 1
+//! use cubic scaling in X and normal linear scaling in Y direction
+#define YUV_SCALER_BICUB_X 2
+//! use cubic scaling without additional lookup texture
+#define YUV_SCALER_BICUB_NOTEX 3
+#define YUV_SCALER_UNSHARP 4
+#define YUV_SCALER_UNSHARP2 5
+//! mask for conversion type
+#define YUV_CONVERSION_MASK 0xF
+//! mask for scaler type
+#define YUV_SCALER_MASK 0xF
+//! shift value for luminance scaler type
+#define YUV_LUM_SCALER_SHIFT 8
+//! shift value for chrominance scaler type
+#define YUV_CHROM_SCALER_SHIFT 12
+//! extract conversion out of type
+#define YUV_CONVERSION(t) ((t) & YUV_CONVERSION_MASK)
+//! extract luminance scaler out of type
+#define YUV_LUM_SCALER(t) (((t) >> YUV_LUM_SCALER_SHIFT) & YUV_SCALER_MASK)
+//! extract chrominance scaler out of type
+#define YUV_CHROM_SCALER(t) (((t) >> YUV_CHROM_SCALER_SHIFT) & YUV_SCALER_MASK)
+#define SET_YUV_CONVERSION(c) ((c) & YUV_CONVERSION_MASK)
+#define SET_YUV_LUM_SCALER(s) (((s) & YUV_SCALER_MASK) << YUV_LUM_SCALER_SHIFT)
+#define SET_YUV_CHROM_SCALER(s) (((s) & YUV_SCALER_MASK) << YUV_CHROM_SCALER_SHIFT)
+//! returns whether the yuv conversion supports large brightness range etc.
+static inline int glYUVLargeRange(int conv)
+{
+ switch (conv) {
+ case YUV_CONVERSION_NONE:
+ case YUV_CONVERSION_COMBINERS_ATI:
+ case YUV_CONVERSION_FRAGMENT_LOOKUP3D:
+ case YUV_CONVERSION_TEXT_FRAGMENT:
+ return 0;
+ }
+ return 1;
+}
+/** \} */
+
+typedef struct {
+ GLenum target;
+ int type;
+ struct mp_csp_params csp_params;
+ int texw;
+ int texh;
+ int chrom_texw;
+ int chrom_texh;
+ float filter_strength;
+ float noise_strength;
+} gl_conversion_params_t;
+
+int glAutodetectYUVConversion(GL *gl);
+void glSetupYUVConversion(GL *gl, gl_conversion_params_t *params);
+void glEnableYUVConversion(GL *gl, GLenum target, int type);
+void glDisableYUVConversion(GL *gl, GLenum target, int type);
+
+#define GL_3D_RED_CYAN 1
+#define GL_3D_GREEN_MAGENTA 2
+#define GL_3D_QUADBUFFER 3
+
+void glEnable3DLeft(GL *gl, int type);
+void glEnable3DRight(GL *gl, int type);
+void glDisable3D(GL *gl, int type);
+
+enum MPGLType {
+ GLTYPE_AUTO,
+ GLTYPE_COCOA,
+ GLTYPE_W32,
+ GLTYPE_X11,
+};
+
+enum {
+ MPGL_CAP_GL = (1 << 0), // GL was successfully loaded
+ MPGL_CAP_GL_LEGACY = (1 << 1), // GL 1.1 (but not 3.x)
+ MPGL_CAP_GL2 = (1 << 2), // GL 2.0 (3.x core subset)
+ MPGL_CAP_GL21 = (1 << 3), // GL 2.1 (3.x core subset)
+ MPGL_CAP_GL3 = (1 << 4), // GL 3.x core
+ MPGL_CAP_FB = (1 << 5),
+ MPGL_CAP_VAO = (1 << 6),
+ MPGL_CAP_SRGB_TEX = (1 << 7),
+ MPGL_CAP_SRGB_FB = (1 << 8),
+ MPGL_CAP_FLOAT_TEX = (1 << 9),
+ MPGL_CAP_TEX_RG = (1 << 10), // GL_ARB_texture_rg / GL 3.x
+ MPGL_CAP_NO_SW = (1 << 30), // used to block sw. renderers
+};
+
+#define MPGL_VER(major, minor) (((major) << 16) | (minor))
+#define MPGL_VER_GET_MAJOR(ver) ((ver) >> 16)
+#define MPGL_VER_GET_MINOR(ver) ((ver) & ((1 << 16) - 1))
+
+#define MPGL_VER_P(ver) MPGL_VER_GET_MAJOR(ver), MPGL_VER_GET_MINOR(ver)
+
+typedef struct MPGLContext {
+ GL *gl;
+ enum MPGLType type;
+ struct vo *vo;
+
+ // Bit size of each component in the created framebuffer. 0 if unknown.
+ int depth_r, depth_g, depth_b;
+
+ // GL version requested from create_window_gl3 backend.
+ // (Might be different from the actual version in gl->version.)
+ int requested_gl_version;
+
+ void (*swapGlBuffers)(struct MPGLContext *);
+ int (*check_events)(struct vo *vo);
+ void (*fullscreen)(struct vo *vo);
+ int (*vo_init)(struct vo *vo);
+ void (*vo_uninit)(struct vo *vo);
+ void (*releaseGlContext)(struct MPGLContext *);
+
+ // Creates GL 1.x/2.x legacy context.
+ bool (*create_window_old)(struct MPGLContext *ctx, uint32_t d_width,
+ uint32_t d_height, uint32_t flags);
+
+ // Creates GL 3.x core context.
+ bool (*create_window_gl3)(struct MPGLContext *ctx, uint32_t d_width,
+ uint32_t d_height, uint32_t flags);
+
+ // optional
+ void (*pause)(struct vo *vo);
+ void (*resume)(struct vo *vo);
+ void (*ontop)(struct vo *vo);
+ void (*border)(struct vo *vo);
+ void (*update_xinerama_info)(struct vo *vo);
+
+ // For free use by the backend.
+ void *priv;
+ // Internal to gl_common.c.
+ bool (*selected_create_window)(struct MPGLContext *ctx, uint32_t d_width,
+ uint32_t d_height, uint32_t flags);
+ bool vo_init_ok;
+} MPGLContext;
+
+int mpgl_find_backend(const char *name);
+
+MPGLContext *mpgl_init(enum MPGLType type, struct vo *vo);
+void mpgl_uninit(MPGLContext *ctx);
+
+// Create a VO window and create a GL context on it.
+// (Calls create_window_gl3 or create_window+setGlWindow.)
+// gl_caps: bitfield of MPGL_CAP_* (required GL version and feature set)
+// flags: passed to the backend's create window function
+// Returns success.
+bool mpgl_create_window(struct MPGLContext *ctx, int gl_caps, uint32_t d_width,
+ uint32_t d_height, uint32_t flags);
+
+// Destroy the window, without resetting GL3 vs. GL2 context choice.
+// If this fails (false), mpgl_uninit(ctx) must be called.
+bool mpgl_destroy_window(struct MPGLContext *ctx);
+
+// print a multi line string with line numbers (e.g. for shader sources)
+// mod, lev: module and log level, as in mp_msg()
+void mp_log_source(int mod, int lev, const char *src);
+
+//function pointers loaded from the OpenGL library
+struct GL {
+ int version; // MPGL_VER() mangled
+ int glsl_version; // e.g. 130 for GLSL 1.30
+ char *extensions; // Equivalent to GL_EXTENSIONS
+ int mpgl_caps; // Bitfield of MPGL_CAP_* constants
+
+ void (GLAPIENTRY *Begin)(GLenum);
+ void (GLAPIENTRY *End)(void);
+ void (GLAPIENTRY *Viewport)(GLint, GLint, GLsizei, GLsizei);
+ void (GLAPIENTRY *MatrixMode)(GLenum);
+ void (GLAPIENTRY *LoadIdentity)(void);
+ void (GLAPIENTRY *Translated)(double, double, double);
+ void (GLAPIENTRY *Scaled)(double, double, double);
+ void (GLAPIENTRY *Ortho)(double, double, double, double, double,double);
+ void (GLAPIENTRY *PushMatrix)(void);
+ void (GLAPIENTRY *PopMatrix)(void);
+ void (GLAPIENTRY *Clear)(GLbitfield);
+ GLuint (GLAPIENTRY *GenLists)(GLsizei);
+ void (GLAPIENTRY *DeleteLists)(GLuint, GLsizei);
+ void (GLAPIENTRY *NewList)(GLuint, GLenum);
+ void (GLAPIENTRY *EndList)(void);
+ void (GLAPIENTRY *CallList)(GLuint);
+ void (GLAPIENTRY *CallLists)(GLsizei, GLenum, const GLvoid *);
+ void (GLAPIENTRY *GenTextures)(GLsizei, GLuint *);
+ void (GLAPIENTRY *DeleteTextures)(GLsizei, const GLuint *);
+ void (GLAPIENTRY *TexEnvi)(GLenum, GLenum, GLint);
+ void (GLAPIENTRY *Color4ub)(GLubyte, GLubyte, GLubyte, GLubyte);
+ void (GLAPIENTRY *Color4f)(GLfloat, GLfloat, GLfloat, GLfloat);
+ void (GLAPIENTRY *ClearColor)(GLclampf, GLclampf, GLclampf, GLclampf);
+ void (GLAPIENTRY *Enable)(GLenum);
+ void (GLAPIENTRY *Disable)(GLenum);
+ const GLubyte *(GLAPIENTRY * GetString)(GLenum);
+ void (GLAPIENTRY *DrawBuffer)(GLenum);
+ void (GLAPIENTRY *DepthMask)(GLboolean);
+ void (GLAPIENTRY *BlendFunc)(GLenum, GLenum);
+ void (GLAPIENTRY *Flush)(void);
+ void (GLAPIENTRY *Finish)(void);
+ void (GLAPIENTRY *PixelStorei)(GLenum, GLint);
+ void (GLAPIENTRY *TexImage1D)(GLenum, GLint, GLint, GLsizei, GLint,
+ GLenum, GLenum, const GLvoid *);
+ void (GLAPIENTRY *TexImage2D)(GLenum, GLint, GLint, GLsizei, GLsizei,
+ GLint, GLenum, GLenum, const GLvoid *);
+ void (GLAPIENTRY *TexSubImage2D)(GLenum, GLint, GLint, GLint,
+ GLsizei, GLsizei, GLenum, GLenum,
+ const GLvoid *);
+ void (GLAPIENTRY *GetTexImage)(GLenum, GLint, GLenum, GLenum, GLvoid *);
+ void (GLAPIENTRY *TexParameteri)(GLenum, GLenum, GLint);
+ void (GLAPIENTRY *TexParameterf)(GLenum, GLenum, GLfloat);
+ void (GLAPIENTRY *TexParameterfv)(GLenum, GLenum, const GLfloat *);
+ void (GLAPIENTRY *TexCoord2f)(GLfloat, GLfloat);
+ void (GLAPIENTRY *TexCoord2fv)(const GLfloat *);
+ void (GLAPIENTRY *Vertex2f)(GLfloat, GLfloat);
+ void (GLAPIENTRY *GetIntegerv)(GLenum, GLint *);
+ void (GLAPIENTRY *GetBooleanv)(GLenum, GLboolean *);
+ void (GLAPIENTRY *ColorMask)(GLboolean, GLboolean, GLboolean, GLboolean);
+ void (GLAPIENTRY *ReadPixels)(GLint, GLint, GLsizei, GLsizei, GLenum,
+ GLenum, GLvoid *);
+ void (GLAPIENTRY *ReadBuffer)(GLenum);
+ void (GLAPIENTRY *VertexPointer)(GLint, GLenum, GLsizei, const GLvoid *);
+ void (GLAPIENTRY *ColorPointer)(GLint, GLenum, GLsizei, const GLvoid *);
+ void (GLAPIENTRY *TexCoordPointer)(GLint, GLenum, GLsizei, const GLvoid *);
+ void (GLAPIENTRY *DrawArrays)(GLenum, GLint, GLsizei);
+ void (GLAPIENTRY *EnableClientState)(GLenum);
+ void (GLAPIENTRY *DisableClientState)(GLenum);
+ GLenum (GLAPIENTRY *GetError)(void);
+
+ void (GLAPIENTRY *GenBuffers)(GLsizei, GLuint *);
+ void (GLAPIENTRY *DeleteBuffers)(GLsizei, const GLuint *);
+ void (GLAPIENTRY *BindBuffer)(GLenum, GLuint);
+ GLvoid * (GLAPIENTRY * MapBuffer)(GLenum, GLenum);
+ GLboolean (GLAPIENTRY *UnmapBuffer)(GLenum);
+ void (GLAPIENTRY *BufferData)(GLenum, intptr_t, const GLvoid *, GLenum);
+ void (GLAPIENTRY *ActiveTexture)(GLenum);
+ void (GLAPIENTRY *BindTexture)(GLenum, GLuint);
+ void (GLAPIENTRY *MultiTexCoord2f)(GLenum, GLfloat, GLfloat);
+ void (GLAPIENTRY *GenPrograms)(GLsizei, GLuint *);
+ void (GLAPIENTRY *DeletePrograms)(GLsizei, const GLuint *);
+ void (GLAPIENTRY *BindProgram)(GLenum, GLuint);
+ void (GLAPIENTRY *ProgramString)(GLenum, GLenum, GLsizei, const GLvoid *);
+ void (GLAPIENTRY *GetProgramivARB)(GLenum, GLenum, GLint *);
+ void (GLAPIENTRY *ProgramEnvParameter4f)(GLenum, GLuint, GLfloat, GLfloat,
+ GLfloat, GLfloat);
+ int (GLAPIENTRY *SwapInterval)(int);
+ void (GLAPIENTRY *TexImage3D)(GLenum, GLint, GLenum, GLsizei, GLsizei,
+ GLsizei, GLint, GLenum, GLenum,
+ const GLvoid *);
+
+ void (GLAPIENTRY *BeginFragmentShader)(void);
+ void (GLAPIENTRY *EndFragmentShader)(void);
+ void (GLAPIENTRY *SampleMap)(GLuint, GLuint, GLenum);
+ void (GLAPIENTRY *ColorFragmentOp2)(GLenum, GLuint, GLuint, GLuint, GLuint,
+ GLuint, GLuint, GLuint, GLuint, GLuint);
+ void (GLAPIENTRY *ColorFragmentOp3)(GLenum, GLuint, GLuint, GLuint, GLuint,
+ GLuint, GLuint, GLuint, GLuint, GLuint,
+ GLuint, GLuint, GLuint);
+ void (GLAPIENTRY *SetFragmentShaderConstant)(GLuint, const GLfloat *);
+
+ void (GLAPIENTRY *GenVertexArrays)(GLsizei, GLuint *);
+ void (GLAPIENTRY *BindVertexArray)(GLuint);
+ GLint (GLAPIENTRY *GetAttribLocation)(GLuint, const GLchar *);
+ void (GLAPIENTRY *EnableVertexAttribArray)(GLuint);
+ void (GLAPIENTRY *DisableVertexAttribArray)(GLuint);
+ void (GLAPIENTRY *VertexAttribPointer)(GLuint, GLint, GLenum, GLboolean,
+ GLsizei, const GLvoid *);
+ void (GLAPIENTRY *DeleteVertexArrays)(GLsizei, const GLuint *);
+ void (GLAPIENTRY *UseProgram)(GLuint);
+ GLint (GLAPIENTRY *GetUniformLocation)(GLuint, const GLchar *);
+ void (GLAPIENTRY *CompileShader)(GLuint);
+ GLuint (GLAPIENTRY *CreateProgram)(void);
+ GLuint (GLAPIENTRY *CreateShader)(GLenum);
+ void (GLAPIENTRY *ShaderSource)(GLuint, GLsizei, const GLchar **,
+ const GLint *);
+ void (GLAPIENTRY *LinkProgram)(GLuint);
+ void (GLAPIENTRY *AttachShader)(GLuint, GLuint);
+ void (GLAPIENTRY *DeleteShader)(GLuint);
+ void (GLAPIENTRY *DeleteProgram)(GLuint);
+ void (GLAPIENTRY *GetShaderInfoLog)(GLuint, GLsizei, GLsizei *, GLchar *);
+ void (GLAPIENTRY *GetShaderiv)(GLuint, GLenum, GLint *);
+ void (GLAPIENTRY *GetProgramInfoLog)(GLuint, GLsizei, GLsizei *, GLchar *);
+ void (GLAPIENTRY *GetProgramiv)(GLenum, GLenum, GLint *);
+ const GLubyte* (GLAPIENTRY *GetStringi)(GLenum, GLuint);
+ void (GLAPIENTRY *BindAttribLocation)(GLuint, GLuint, const GLchar *);
+ void (GLAPIENTRY *BindFramebuffer)(GLenum, GLuint);
+ void (GLAPIENTRY *GenFramebuffers)(GLsizei, GLuint *);
+ void (GLAPIENTRY *DeleteFramebuffers)(GLsizei, const GLuint *);
+ GLenum (GLAPIENTRY *CheckFramebufferStatus)(GLenum);
+ void (GLAPIENTRY *FramebufferTexture2D)(GLenum, GLenum, GLenum, GLuint,
+ GLint);
+
+ void (GLAPIENTRY *Uniform1f)(GLint, GLfloat);
+ void (GLAPIENTRY *Uniform2f)(GLint, GLfloat, GLfloat);
+ void (GLAPIENTRY *Uniform3f)(GLint, GLfloat, GLfloat, GLfloat);
+ void (GLAPIENTRY *Uniform4f)(GLint, GLfloat, GLfloat, GLfloat, GLfloat);
+ void (GLAPIENTRY *Uniform1i)(GLint, GLint);
+ void (GLAPIENTRY *UniformMatrix3fv)(GLint, GLsizei, GLboolean,
+ const GLfloat *);
+ void (GLAPIENTRY *UniformMatrix4x3fv)(GLint, GLsizei, GLboolean,
+ const GLfloat *);
+};
+
+#endif /* MPLAYER_GL_COMMON_H */
diff --git a/video/out/gl_header_fixes.h b/video/out/gl_header_fixes.h
new file mode 100644
index 0000000000..d149a9970a
--- /dev/null
+++ b/video/out/gl_header_fixes.h
@@ -0,0 +1,257 @@
+/*
+ * 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.
+ */
+
+// workaround for some gl.h headers
+#ifndef GLAPIENTRY
+#ifdef APIENTRY
+#define GLAPIENTRY APIENTRY
+#elif defined(CONFIG_GL_WIN32)
+#define GLAPIENTRY __stdcall
+#else
+#define GLAPIENTRY
+#endif
+#endif
+
+/**
+ * \defgroup glextdefines OpenGL extension defines
+ *
+ * conditionally define all extension defines used.
+ * vendor specific extensions should be marked as such
+ * (e.g. _NV), _ARB is not used to ease readability.
+ * \{
+ */
+#ifndef GL_TEXTURE_3D
+#define GL_TEXTURE_3D 0x806F
+#endif
+#ifndef GL_TEXTURE_WRAP_R
+#define GL_TEXTURE_WRAP_R 0x8072
+#endif
+#ifndef GL_CLAMP_TO_EDGE
+#define GL_CLAMP_TO_EDGE 0x812F
+#endif
+#ifndef GL_GENERATE_MIPMAP
+#define GL_GENERATE_MIPMAP 0x8191
+#endif
+#ifndef GL_TEXT_FRAGMENT_SHADER_ATI
+#define GL_TEXT_FRAGMENT_SHADER_ATI 0x8200
+#endif
+#ifndef GL_FRAGMENT_SHADER_ATI
+#define GL_FRAGMENT_SHADER_ATI 0x8920
+#endif
+#ifndef GL_NUM_FRAGMENT_REGISTERS_ATI
+#define GL_NUM_FRAGMENT_REGISTERS_ATI 0x896E
+#endif
+#ifndef GL_REG_0_ATI
+#define GL_REG_0_ATI 0x8921
+#endif
+#ifndef GL_REG_1_ATI
+#define GL_REG_1_ATI 0x8922
+#endif
+#ifndef GL_REG_2_ATI
+#define GL_REG_2_ATI 0x8923
+#endif
+#ifndef GL_CON_0_ATI
+#define GL_CON_0_ATI 0x8941
+#endif
+#ifndef GL_CON_1_ATI
+#define GL_CON_1_ATI 0x8942
+#endif
+#ifndef GL_CON_2_ATI
+#define GL_CON_2_ATI 0x8943
+#endif
+#ifndef GL_CON_3_ATI
+#define GL_CON_3_ATI 0x8944
+#endif
+#ifndef GL_ADD_ATI
+#define GL_ADD_ATI 0x8963
+#endif
+#ifndef GL_MUL_ATI
+#define GL_MUL_ATI 0x8964
+#endif
+#ifndef GL_MAD_ATI
+#define GL_MAD_ATI 0x8968
+#endif
+#ifndef GL_SWIZZLE_STR_ATI
+#define GL_SWIZZLE_STR_ATI 0x8976
+#endif
+#ifndef GL_4X_BIT_ATI
+#define GL_4X_BIT_ATI 2
+#endif
+#ifndef GL_8X_BIT_ATI
+#define GL_8X_BIT_ATI 4
+#endif
+#ifndef GL_BIAS_BIT_ATI
+#define GL_BIAS_BIT_ATI 8
+#endif
+#ifndef GL_MAX_TEXTURE_UNITS
+#define GL_MAX_TEXTURE_UNITS 0x84E2
+#endif
+#ifndef GL_TEXTURE0
+#define GL_TEXTURE0 0x84C0
+#endif
+#ifndef GL_TEXTURE1
+#define GL_TEXTURE1 0x84C1
+#endif
+#ifndef GL_TEXTURE2
+#define GL_TEXTURE2 0x84C2
+#endif
+#ifndef GL_TEXTURE3
+#define GL_TEXTURE3 0x84C3
+#endif
+#ifndef GL_TEXTURE_RECTANGLE
+#define GL_TEXTURE_RECTANGLE 0x84F5
+#endif
+#ifndef GL_PIXEL_UNPACK_BUFFER
+#define GL_PIXEL_UNPACK_BUFFER 0x88EC
+#endif
+#ifndef GL_STREAM_DRAW
+#define GL_STREAM_DRAW 0x88E0
+#endif
+#ifndef GL_DYNAMIC_DRAW
+#define GL_DYNAMIC_DRAW 0x88E8
+#endif
+#ifndef GL_WRITE_ONLY
+#define GL_WRITE_ONLY 0x88B9
+#endif
+#ifndef GL_BGR
+#define GL_BGR 0x80E0
+#endif
+#ifndef GL_BGRA
+#define GL_BGRA 0x80E1
+#endif
+#ifndef GL_UNSIGNED_BYTE_3_3_2
+#define GL_UNSIGNED_BYTE_3_3_2 0x8032
+#endif
+#ifndef GL_UNSIGNED_BYTE_2_3_3_REV
+#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362
+#endif
+#ifndef GL_UNSIGNED_SHORT_4_4_4_4
+#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033
+#endif
+#ifndef GL_UNSIGNED_SHORT_4_4_4_4_REV
+#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365
+#endif
+#ifndef GL_UNSIGNED_SHORT_5_6_5
+#define GL_UNSIGNED_SHORT_5_6_5 0x8363
+#endif
+#ifndef GL_UNSIGNED_INT_8_8_8_8
+#define GL_UNSIGNED_INT_8_8_8_8 0x8035
+#endif
+#ifndef GL_UNSIGNED_INT_8_8_8_8_REV
+#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
+#endif
+#ifndef GL_UNSIGNED_SHORT_5_6_5_REV
+#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364
+#endif
+#ifndef GL_UNSIGNED_INT_10_10_10_2
+#define GL_UNSIGNED_INT_10_10_10_2 0x8036
+#endif
+#ifndef GL_UNSIGNED_INT_2_10_10_10_REV
+#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
+#endif
+#ifndef GL_UNSIGNED_SHORT_5_5_5_1
+#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034
+#endif
+#ifndef GL_UNSIGNED_SHORT_1_5_5_5_REV
+#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366
+#endif
+#ifndef GL_UNSIGNED_SHORT_8_8
+#define GL_UNSIGNED_SHORT_8_8 0x85BA
+#endif
+#ifndef GL_UNSIGNED_SHORT_8_8_REV
+#define GL_UNSIGNED_SHORT_8_8_REV 0x85BB
+#endif
+#ifndef GL_YCBCR_MESA
+#define GL_YCBCR_MESA 0x8757
+#endif
+#ifndef GL_RGB32F
+#define GL_RGB32F 0x8815
+#endif
+#ifndef GL_FLOAT_RGB32_NV
+#define GL_FLOAT_RGB32_NV 0x8889
+#endif
+#ifndef GL_LUMINANCE16
+#define GL_LUMINANCE16 0x8042
+#endif
+#ifndef GL_R16
+#define GL_R16 0x822A
+#endif
+#ifndef GL_UNPACK_CLIENT_STORAGE_APPLE
+#define GL_UNPACK_CLIENT_STORAGE_APPLE 0x85B2
+#endif
+#ifndef GL_FRAGMENT_PROGRAM
+#define GL_FRAGMENT_PROGRAM 0x8804
+#endif
+#ifndef GL_PROGRAM_FORMAT_ASCII
+#define GL_PROGRAM_FORMAT_ASCII 0x8875
+#endif
+#ifndef GL_PROGRAM_ERROR_POSITION
+#define GL_PROGRAM_ERROR_POSITION 0x864B
+#endif
+#ifndef GL_MAX_TEXTURE_IMAGE_UNITS
+#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872
+#endif
+#ifndef GL_PROGRAM_ERROR_STRING
+#define GL_PROGRAM_ERROR_STRING 0x8874
+#endif
+/** \} */ // end of glextdefines group
+
+
+#if defined(CONFIG_GL_WIN32) && !defined(WGL_CONTEXT_MAJOR_VERSION_ARB)
+/* these are supposed to be defined in wingdi.h but mingw's is too old */
+/* only the bits actually used by mplayer are defined */
+/* reference: http://www.opengl.org/registry/specs/ARB/wgl_create_context.txt */
+
+#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
+#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
+#define WGL_CONTEXT_FLAGS_ARB 0x2094
+#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
+#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
+#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
+#endif
+
+// Define just enough constants to make the OpenGL 3 code compile against
+// older SDKs. Values are taken straight from OpenGL/gl3.h
+#if defined __APPLE__ && !(defined GL_VERSION_3_0)
+#define GL_RGBA16F 0x881A
+#define GL_RGB16F 0x881B
+#define GL_MAJOR_VERSION 0x821B
+#define GL_MINOR_VERSION 0x821C
+#define GL_NUM_EXTENSIONS 0x821D
+
+#ifndef GL_ARB_framebuffer_sRGB
+#define GL_FRAMEBUFFER_SRGB 0x8DB9
+#endif
+#endif
+
+// FreeBSD 10.0-CURRENT lacks the GLX_ARB_create_context extension completely
+#ifndef GLX_CONTEXT_MAJOR_VERSION_ARB
+#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
+#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
+#define GLX_CONTEXT_FLAGS_ARB 0x2094
+#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126
+#define GLX_CONTEXT_DEBUG_BIT_ARB 0x0001
+#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
+#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
+#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
+#endif
diff --git a/video/out/gl_osd.c b/video/out/gl_osd.c
new file mode 100644
index 0000000000..81485cabe9
--- /dev/null
+++ b/video/out/gl_osd.c
@@ -0,0 +1,324 @@
+/*
+ * 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 <stdlib.h>
+#include <assert.h>
+#include <libavutil/common.h>
+
+#include "bitmap_packer.h"
+
+#include "gl_osd.h"
+
+struct osd_fmt_entry {
+ GLint internal_format;
+ GLint format;
+ GLenum type;
+};
+
+// glBlendFunc() arguments
+static const int blend_factors[SUBBITMAP_COUNT][2] = {
+ [SUBBITMAP_LIBASS] = {GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA},
+ [SUBBITMAP_RGBA] = {GL_ONE, GL_ONE_MINUS_SRC_ALPHA},
+};
+
+static const struct osd_fmt_entry osd_to_gl3_formats[SUBBITMAP_COUNT] = {
+ [SUBBITMAP_LIBASS] = {GL_RED, GL_RED, GL_UNSIGNED_BYTE},
+ [SUBBITMAP_RGBA] = {GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE},
+};
+
+static const struct osd_fmt_entry osd_to_gl_legacy_formats[SUBBITMAP_COUNT] = {
+ [SUBBITMAP_LIBASS] = {GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE},
+ [SUBBITMAP_RGBA] = {GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE},
+};
+
+struct mpgl_osd *mpgl_osd_init(GL *gl, bool legacy)
+{
+ GLint max_texture_size;
+ gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
+
+ struct mpgl_osd *ctx = talloc_ptrtype(NULL, ctx);
+ *ctx = (struct mpgl_osd) {
+ .gl = gl,
+ .fmt_table = legacy ? osd_to_gl_legacy_formats : osd_to_gl3_formats,
+ .scratch = talloc_zero_size(ctx, 1),
+ };
+
+ for (int n = 0; n < MAX_OSD_PARTS; n++) {
+ struct mpgl_osd_part *p = talloc_ptrtype(ctx, p);
+ *p = (struct mpgl_osd_part) {
+ .packer = talloc_struct(p, struct bitmap_packer, {
+ .w_max = max_texture_size,
+ .h_max = max_texture_size,
+ }),
+ };
+ ctx->parts[n] = p;
+ }
+
+ for (int n = 0; n < SUBBITMAP_COUNT; n++)
+ ctx->formats[n] = ctx->fmt_table[n].type != 0;
+
+ return ctx;
+}
+
+void mpgl_osd_destroy(struct mpgl_osd *ctx)
+{
+ GL *gl = ctx->gl;
+
+ for (int n = 0; n < MAX_OSD_PARTS; n++) {
+ struct mpgl_osd_part *p = ctx->parts[n];
+ gl->DeleteTextures(1, &p->texture);
+ if (gl->DeleteBuffers)
+ gl->DeleteBuffers(1, &p->buffer);
+ }
+ talloc_free(ctx);
+}
+
+static bool upload_pbo(struct mpgl_osd *ctx, struct mpgl_osd_part *osd,
+ struct sub_bitmaps *imgs)
+{
+ GL *gl = ctx->gl;
+ bool success = true;
+ struct osd_fmt_entry fmt = ctx->fmt_table[imgs->format];
+ int pix_stride = glFmt2bpp(fmt.format, fmt.type);
+
+ if (!osd->buffer) {
+ gl->GenBuffers(1, &osd->buffer);
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, osd->buffer);
+ gl->BufferData(GL_PIXEL_UNPACK_BUFFER, osd->w * osd->h * pix_stride,
+ NULL, GL_DYNAMIC_COPY);
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+ }
+
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, osd->buffer);
+ char *data = gl->MapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
+ if (!data) {
+ success = false;
+ } else {
+ struct pos bb[2];
+ packer_get_bb(osd->packer, bb);
+ size_t stride = osd->w * pix_stride;
+ packer_copy_subbitmaps(osd->packer, imgs, data, pix_stride, stride);
+ if (!gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER))
+ success = false;
+ glUploadTex(gl, GL_TEXTURE_2D, fmt.format, fmt.type, NULL, stride,
+ bb[0].x, bb[0].y, bb[1].x - bb[0].x, bb[1].y - bb[0].y,
+ 0);
+ }
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+
+ if (!success) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Error: can't upload subtitles! "
+ "Remove the 'pbo' suboption.\n");
+ }
+
+ return success;
+}
+
+static void upload_tex(struct mpgl_osd *ctx, struct mpgl_osd_part *osd,
+ struct sub_bitmaps *imgs)
+{
+ struct osd_fmt_entry fmt = ctx->fmt_table[imgs->format];
+ if (osd->packer->padding) {
+ struct pos bb[2];
+ packer_get_bb(osd->packer, bb);
+ glClearTex(ctx->gl, GL_TEXTURE_2D, fmt.format, fmt.type,
+ bb[0].x, bb[0].y, bb[1].x - bb[0].y, bb[1].y - bb[0].y,
+ 0, &ctx->scratch);
+ }
+ for (int n = 0; n < osd->packer->count; n++) {
+ struct sub_bitmap *s = &imgs->parts[n];
+ struct pos p = osd->packer->result[n];
+
+ glUploadTex(ctx->gl, GL_TEXTURE_2D, fmt.format, fmt.type,
+ s->bitmap, s->stride, p.x, p.y, s->w, s->h, 0);
+ }
+}
+
+static bool upload_osd(struct mpgl_osd *ctx, struct mpgl_osd_part *osd,
+ struct sub_bitmaps *imgs)
+{
+ GL *gl = ctx->gl;
+
+ // assume 2x2 filter on scaling
+ osd->packer->padding = ctx->scaled || imgs->scaled;
+ int r = packer_pack_from_subbitmaps(osd->packer, imgs);
+ if (r < 0) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] OSD bitmaps do not fit on "
+ "a surface with the maximum supported size %dx%d.\n",
+ osd->packer->w_max, osd->packer->h_max);
+ return false;
+ }
+
+ struct osd_fmt_entry fmt = ctx->fmt_table[imgs->format];
+ assert(fmt.type != 0);
+
+ if (!osd->texture)
+ gl->GenTextures(1, &osd->texture);
+
+ gl->BindTexture(GL_TEXTURE_2D, osd->texture);
+
+ if (osd->packer->w > osd->w || osd->packer->h > osd->h
+ || osd->format != imgs->format)
+ {
+ osd->format = imgs->format;
+ osd->w = FFMAX(32, osd->packer->w);
+ osd->h = FFMAX(32, osd->packer->h);
+
+ gl->TexImage2D(GL_TEXTURE_2D, 0, fmt.internal_format, osd->w, osd->h,
+ 0, fmt.format, fmt.type, NULL);
+
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ if (gl->DeleteBuffers)
+ gl->DeleteBuffers(1, &osd->buffer);
+ osd->buffer = 0;
+ }
+
+ bool uploaded = false;
+ if (ctx->use_pbo)
+ uploaded = upload_pbo(ctx, osd, imgs);
+ if (!uploaded)
+ upload_tex(ctx, osd, imgs);
+
+ gl->BindTexture(GL_TEXTURE_2D, 0);
+
+ return true;
+}
+
+struct mpgl_osd_part *mpgl_osd_generate(struct mpgl_osd *ctx,
+ struct sub_bitmaps *imgs)
+{
+ if (imgs->num_parts == 0 || !ctx->formats[imgs->format])
+ return NULL;
+
+ struct mpgl_osd_part *osd = ctx->parts[imgs->render_index];
+
+ if (imgs->bitmap_pos_id != osd->bitmap_pos_id) {
+ if (imgs->bitmap_id != osd->bitmap_id) {
+ if (!upload_osd(ctx, osd, imgs))
+ osd->packer->count = 0;
+ }
+
+ osd->bitmap_id = imgs->bitmap_id;
+ osd->bitmap_pos_id = imgs->bitmap_pos_id;
+ osd->num_vertices = 0;
+ }
+
+ return osd->packer->count ? osd : NULL;
+}
+
+void mpgl_osd_set_gl_state(struct mpgl_osd *ctx, struct mpgl_osd_part *p)
+{
+ GL *gl = ctx->gl;
+
+ gl->BindTexture(GL_TEXTURE_2D, p->texture);
+ gl->Enable(GL_BLEND);
+ gl->BlendFunc(blend_factors[p->format][0], blend_factors[p->format][1]);
+}
+
+void mpgl_osd_unset_gl_state(struct mpgl_osd *ctx, struct mpgl_osd_part *p)
+{
+ GL *gl = ctx->gl;
+
+ gl->Disable(GL_BLEND);
+ gl->BindTexture(GL_TEXTURE_2D, 0);
+}
+
+struct vertex {
+ float position[2];
+ uint8_t color[4];
+ float texcoord[2];
+};
+
+static void draw_legacy_cb(void *pctx, struct sub_bitmaps *imgs)
+{
+ struct mpgl_osd *ctx = pctx;
+ struct mpgl_osd_part *osd = mpgl_osd_generate(ctx, imgs);
+ if (!osd)
+ return;
+
+ if (!osd->num_vertices) {
+ // 2 triangles primitives per quad = 6 vertices per quad
+ // not using GL_QUADS, as it is deprecated in OpenGL 3.x and later
+ osd->vertices = talloc_realloc(osd, osd->vertices, struct vertex,
+ osd->packer->count * 6);
+
+ struct vertex *va = osd->vertices;
+ float tex_w = osd->w;
+ float tex_h = osd->h;
+
+ for (int n = 0; n < osd->packer->count; n++) {
+ struct sub_bitmap *b = &imgs->parts[n];
+ struct pos p = osd->packer->result[n];
+
+ uint32_t c = imgs->format == SUBBITMAP_LIBASS
+ ? b->libass.color : 0xFFFFFF00;
+ uint8_t color[4] = { c >> 24, (c >> 16) & 0xff,
+ (c >> 8) & 0xff, 255 - (c & 0xff) };
+
+ float x0 = b->x;
+ float y0 = b->y;
+ float x1 = b->x + b->dw;
+ float y1 = b->y + b->dh;
+ float tx0 = p.x / tex_w;
+ float ty0 = p.y / tex_h;
+ float tx1 = (p.x + b->w) / tex_w;
+ float ty1 = (p.y + b->h) / tex_h;
+
+#define COLOR_INIT {color[0], color[1], color[2], color[3]}
+ struct vertex *v = &va[osd->num_vertices];
+ v[0] = (struct vertex) { {x0, y0}, COLOR_INIT, {tx0, ty0} };
+ v[1] = (struct vertex) { {x0, y1}, COLOR_INIT, {tx0, ty1} };
+ v[2] = (struct vertex) { {x1, y0}, COLOR_INIT, {tx1, ty0} };
+ v[3] = (struct vertex) { {x1, y1}, COLOR_INIT, {tx1, ty1} };
+ v[4] = v[2];
+ v[5] = v[1];
+#undef COLOR_INIT
+ osd->num_vertices += 6;
+ }
+ }
+
+ GL *gl = ctx->gl;
+
+ struct vertex *va = osd->vertices;
+ size_t stride = sizeof(va[0]);
+
+ gl->VertexPointer(2, GL_FLOAT, stride, &va[0].position[0]);
+ gl->ColorPointer(4, GL_UNSIGNED_BYTE, stride, &va[0].color[0]);
+ gl->TexCoordPointer(2, GL_FLOAT, stride, &va[0].texcoord[0]);
+
+ gl->EnableClientState(GL_VERTEX_ARRAY);
+ gl->EnableClientState(GL_TEXTURE_COORD_ARRAY);
+ gl->EnableClientState(GL_COLOR_ARRAY);
+
+ mpgl_osd_set_gl_state(ctx, osd);
+ gl->DrawArrays(GL_TRIANGLES, 0, osd->num_vertices);
+ mpgl_osd_unset_gl_state(ctx, osd);
+
+ gl->DisableClientState(GL_VERTEX_ARRAY);
+ gl->DisableClientState(GL_TEXTURE_COORD_ARRAY);
+ gl->DisableClientState(GL_COLOR_ARRAY);
+}
+
+void mpgl_osd_draw_legacy(struct mpgl_osd *ctx, struct osd_state *osd,
+ struct mp_osd_res res)
+{
+ osd_draw(osd, res, osd->vo_pts, 0, ctx->formats, draw_legacy_cb, ctx);
+}
diff --git a/video/out/gl_osd.h b/video/out/gl_osd.h
new file mode 100644
index 0000000000..cf3182ffb2
--- /dev/null
+++ b/video/out/gl_osd.h
@@ -0,0 +1,43 @@
+#ifndef MPLAYER_GL_OSD_H
+#define MPLAYER_GL_OSD_H
+
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include "gl_common.h"
+#include "sub/sub.h"
+
+struct mpgl_osd_part {
+ enum sub_bitmap_format format;
+ int bitmap_id, bitmap_pos_id;
+ GLuint texture;
+ int w, h;
+ GLuint buffer;
+ int num_vertices;
+ void *vertices;
+ struct bitmap_packer *packer;
+};
+
+struct mpgl_osd {
+ GL *gl;
+ bool use_pbo;
+ bool scaled;
+ struct mpgl_osd_part *parts[MAX_OSD_PARTS];
+ const struct osd_fmt_entry *fmt_table;
+ bool formats[SUBBITMAP_COUNT];
+ void *scratch;
+};
+
+struct mpgl_osd *mpgl_osd_init(GL *gl, bool legacy);
+void mpgl_osd_destroy(struct mpgl_osd *ctx);
+
+struct mpgl_osd_part *mpgl_osd_generate(struct mpgl_osd *ctx,
+ struct sub_bitmaps *b);
+
+void mpgl_osd_set_gl_state(struct mpgl_osd *ctx, struct mpgl_osd_part *p);
+void mpgl_osd_unset_gl_state(struct mpgl_osd *ctx, struct mpgl_osd_part *p);
+
+void mpgl_osd_draw_legacy(struct mpgl_osd *ctx, struct osd_state *osd,
+ struct mp_osd_res res);
+
+#endif
diff --git a/video/out/osx_common.c b/video/out/osx_common.c
new file mode 100644
index 0000000000..2aa0a28126
--- /dev/null
+++ b/video/out/osx_common.c
@@ -0,0 +1,145 @@
+/*
+ * 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"
+
+// only to get keycode definitions from HIToolbox/Events.h
+#include <Carbon/Carbon.h>
+#include <CoreServices/CoreServices.h>
+#include "config.h"
+#include "osx_common.h"
+#include "video_out.h"
+#include "input/keycodes.h"
+#include "input/input.h"
+#include "mp_msg.h"
+
+/*
+ * Define keycodes only found in OSX >= 10.5 for older versions
+ */
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= 1040
+#define kVK_ANSI_Keypad0 0x52
+#define kVK_ANSI_Keypad1 0x53
+#define kVK_ANSI_Keypad2 0x54
+#define kVK_ANSI_Keypad3 0x55
+#define kVK_ANSI_Keypad4 0x56
+#define kVK_ANSI_Keypad5 0x57
+#define kVK_ANSI_Keypad6 0x58
+#define kVK_ANSI_Keypad7 0x59
+#define kVK_ANSI_Keypad8 0x5b
+#define kVK_ANSI_Keypad9 0x5c
+#define kVK_ANSI_KeypadDecimal 0x41
+#define kVK_ANSI_KeypadDivide 0x4b
+#define kVK_ANSI_KeypadEnter 0x4c
+#define kVK_ANSI_KeypadMinus 0x4e
+#define kVK_ANSI_KeypadMultiply 0x43
+#define kVK_ANSI_KeypadPlus 0x45
+#define kVK_Control 0x3b
+#define kVK_Delete 0x33
+#define kVK_DownArrow 0x7d
+#define kVK_End 0x77
+#define kVK_Escape 0x35
+#define kVK_F1 0x7a
+#define kVK_F10 0x6d
+#define kVK_F11 0x67
+#define kVK_F12 0x6f
+#define kVK_F2 0x78
+#define kVK_F3 0x63
+#define kVK_F4 0x76
+#define kVK_F5 0x60
+#define kVK_F6 0x61
+#define kVK_F7 0x62
+#define kVK_F8 0x64
+#define kVK_F9 0x65
+#define kVK_ForwardDelete 0x75
+#define kVK_Help 0x72
+#define kVK_Home 0x73
+#define kVK_LeftArrow 0x7b
+#define kVK_Option 0x3a
+#define kVK_PageDown 0x79
+#define kVK_PageUp 0x74
+#define kVK_Return 0x24
+#define kVK_RightArrow 0x7c
+#define kVK_Shift 0x38
+#define kVK_Tab 0x30
+#define kVK_UpArrow 0x7e
+#endif /* MAC_OS_X_VERSION_MAX_ALLOWED <= 1040 */
+
+static const struct mp_keymap keymap[] = {
+ // special keys
+ {0x34, KEY_ENTER}, // Enter key on some iBooks?
+ {kVK_Return, KEY_ENTER},
+ {kVK_Escape, KEY_ESC},
+ {kVK_Delete, KEY_BACKSPACE}, {kVK_Option, KEY_BACKSPACE}, {kVK_Control, KEY_BACKSPACE}, {kVK_Shift, KEY_BACKSPACE},
+ {kVK_Tab, KEY_TAB},
+
+ // cursor keys
+ {kVK_UpArrow, KEY_UP}, {kVK_DownArrow, KEY_DOWN}, {kVK_LeftArrow, KEY_LEFT}, {kVK_RightArrow, KEY_RIGHT},
+
+ // navigation block
+ {kVK_Help, KEY_INSERT}, {kVK_ForwardDelete, KEY_DELETE}, {kVK_Home, KEY_HOME},
+ {kVK_End, KEY_END}, {kVK_PageUp, KEY_PAGE_UP}, {kVK_PageDown, KEY_PAGE_DOWN},
+
+ // F-keys
+ {kVK_F1, KEY_F + 1}, {kVK_F2, KEY_F + 2}, {kVK_F3, KEY_F + 3}, {kVK_F4, KEY_F + 4},
+ {kVK_F5, KEY_F + 5}, {kVK_F6, KEY_F + 6}, {kVK_F7, KEY_F + 7}, {kVK_F8, KEY_F + 8},
+ {kVK_F9, KEY_F + 9}, {kVK_F10, KEY_F + 10}, {kVK_F11, KEY_F + 11}, {kVK_F12, KEY_F + 12},
+
+ // numpad
+ {kVK_ANSI_KeypadPlus, '+'}, {kVK_ANSI_KeypadMinus, '-'}, {kVK_ANSI_KeypadMultiply, '*'},
+ {kVK_ANSI_KeypadDivide, '/'}, {kVK_ANSI_KeypadEnter, KEY_KPENTER}, {kVK_ANSI_KeypadDecimal, KEY_KPDEC},
+ {kVK_ANSI_Keypad0, KEY_KP0}, {kVK_ANSI_Keypad1, KEY_KP1}, {kVK_ANSI_Keypad2, KEY_KP2}, {kVK_ANSI_Keypad3, KEY_KP3},
+ {kVK_ANSI_Keypad4, KEY_KP4}, {kVK_ANSI_Keypad5, KEY_KP5}, {kVK_ANSI_Keypad6, KEY_KP6}, {kVK_ANSI_Keypad7, KEY_KP7},
+ {kVK_ANSI_Keypad8, KEY_KP8}, {kVK_ANSI_Keypad9, KEY_KP9},
+
+ {0, 0}
+};
+
+int convert_key(unsigned key, unsigned charcode)
+{
+ int mpkey = lookup_keymap_table(keymap, key);
+ if (mpkey)
+ return mpkey;
+ return charcode;
+}
+
+/**
+ * Checks at runtime that OSX version is the same or newer than the one
+ * provided as input.
+ */
+int is_osx_version_at_least(int majorv, int minorv, int bugfixv)
+{
+ OSErr err;
+ SInt32 major, minor, bugfix;
+ if ((err = Gestalt(gestaltSystemVersionMajor, &major)) != noErr)
+ goto fail;
+ if ((err = Gestalt(gestaltSystemVersionMinor, &minor)) != noErr)
+ goto fail;
+ if ((err = Gestalt(gestaltSystemVersionBugFix, &bugfix)) != noErr)
+ goto fail;
+
+ if(major > majorv ||
+ (major == majorv && (minor > minorv ||
+ (minor == minorv && bugfix >= bugfixv))))
+ return 1;
+ else
+ return 0;
+fail:
+ // There's no reason the Gestalt system call should fail on OSX.
+ mp_msg(MSGT_VO, MSGL_FATAL, "[osx] Failed to get system version number. "
+ "Please contact the developers. Error code: %ld\n", (long)err);
+ return 0;
+}
diff --git a/video/out/osx_common.h b/video/out/osx_common.h
new file mode 100644
index 0000000000..ae31a6353d
--- /dev/null
+++ b/video/out/osx_common.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_OSX_COMMON_H
+#define MPLAYER_OSX_COMMON_H
+
+struct vo;
+
+int convert_key(unsigned key, unsigned charcode);
+int is_osx_version_at_least(int majorv, int minorv, int bugfixv);
+
+#endif /* MPLAYER_OSX_COMMON_H */
diff --git a/video/out/pnm_loader.c b/video/out/pnm_loader.c
new file mode 100644
index 0000000000..048461e51f
--- /dev/null
+++ b/video/out/pnm_loader.c
@@ -0,0 +1,97 @@
+/*
+ * PNM image files loader
+ *
+ * copyleft (C) 2005-2010 Reimar Döffinger <Reimar.Doeffinger@gmx.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.
+ *
+ * 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 pnm_loader.c
+ * \brief PNM image files loader
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "pnm_loader.h"
+
+/**
+ * \brief skips whitespace and comments
+ * \param f file to read from
+ */
+static void ppm_skip(FILE *f) {
+ int c, comment = 0;
+ do {
+ c = fgetc(f);
+ if (c == '#')
+ comment = 1;
+ if (c == '\n')
+ comment = 0;
+ } while (c != EOF && (isspace(c) || comment));
+ if (c != EOF)
+ ungetc(c, f);
+}
+
+#define MAXDIM (16 * 1024)
+
+uint8_t *read_pnm(FILE *f, int *width, int *height,
+ int *bytes_per_pixel, int *maxval) {
+ uint8_t *data;
+ int type;
+ unsigned w, h, m, val, bpp;
+ *width = *height = *bytes_per_pixel = *maxval = 0;
+ ppm_skip(f);
+ if (fgetc(f) != 'P')
+ return NULL;
+ type = fgetc(f);
+ if (type != '5' && type != '6')
+ return NULL;
+ ppm_skip(f);
+ if (fscanf(f, "%u", &w) != 1)
+ return NULL;
+ ppm_skip(f);
+ if (fscanf(f, "%u", &h) != 1)
+ return NULL;
+ ppm_skip(f);
+ if (fscanf(f, "%u", &m) != 1)
+ return NULL;
+ val = fgetc(f);
+ if (!isspace(val))
+ return NULL;
+ if (w > MAXDIM || h > MAXDIM)
+ return NULL;
+ bpp = (m > 255) ? 2 : 1;
+ if (type == '6')
+ bpp *= 3;
+ data = malloc(w * h * bpp);
+ if (fread(data, w * bpp, h, f) != h) {
+ free(data);
+ return NULL;
+ }
+ *width = w;
+ *height = h;
+ *bytes_per_pixel = bpp;
+ *maxval = m;
+ return data;
+}
diff --git a/video/out/pnm_loader.h b/video/out/pnm_loader.h
new file mode 100644
index 0000000000..e00cce2e63
--- /dev/null
+++ b/video/out/pnm_loader.h
@@ -0,0 +1,52 @@
+/*
+ * PNM image files loader
+ *
+ * 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_PNM_LOADER_H
+#define MPLAYER_PNM_LOADER_H
+
+#include <stdio.h>
+#include <stdint.h>
+
+/**
+ * Read a "portable anymap" image.
+ * Supports raw PGM (P5) and PNM (P6).
+ *
+ * @param[in] f input stream.
+ * @param[out] width width of the loaded image.
+ * @param[out] height height of the loaded image.
+ * @param[out] bytes_per_pixel format of the loaded image.
+ * @param[out] maxval maximum pixel value; possible values are:
+ * 1 for 8 bits gray,
+ * 2 for 16 bits gray,
+ * 3 for 8 bits per component RGB,
+ * 6 for 16 bits per component RGB.
+ * @return a newly allocated array of
+ * width*height*bytes_per_pixel bytes,
+ * or NULL in case of error.
+ */
+uint8_t *read_pnm(FILE *f, int *width, int *height,
+ int *bytes_per_pixel, int *maxval);
+
+#endif /* MPLAYER_PNM_LOADER_H */
diff --git a/video/out/vo.c b/video/out/vo.c
new file mode 100644
index 0000000000..571f00da4d
--- /dev/null
+++ b/video/out/vo.c
@@ -0,0 +1,530 @@
+/*
+ * libvo common functions, variables used by many/all drivers.
+ *
+ * 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 <stdbool.h>
+
+#include <unistd.h>
+//#include <sys/mman.h>
+
+#include "config.h"
+#include "options.h"
+#include "talloc.h"
+#include "bstr.h"
+#include "video_out.h"
+#include "aspect.h"
+#include "geometry.h"
+#include "input/input.h"
+#include "mp_fifo.h"
+#include "m_config.h"
+#include "mp_msg.h"
+#include "libmpcodecs/vfcap.h"
+#include "sub/sub.h"
+
+#include "osdep/shmem.h"
+#ifdef CONFIG_X11
+#include "x11_common.h"
+#endif
+
+int xinerama_screen = -1;
+int xinerama_x;
+int xinerama_y;
+
+int vo_nomouse_input = 0;
+int vo_grabpointer = 1;
+int vo_vsync = 1;
+int vo_fs = 0;
+int vo_fsmode = 0;
+float vo_panscan = 0.0f;
+int vo_refresh_rate=0;
+int vo_keepaspect=1;
+int vo_rootwin=0;
+int vo_border=1;
+int64_t WinID = -1;
+
+int vo_pts=0; // for hw decoding
+float vo_fps=0;
+
+int vo_colorkey = 0x0000ff00; // default colorkey is green
+ // (0xff000000 means that colorkey has been disabled)
+
+//
+// Externally visible list of all vo drivers
+//
+extern struct vo_driver video_out_x11;
+extern struct vo_driver video_out_vdpau;
+extern struct vo_driver video_out_xv;
+extern struct vo_driver video_out_opengl;
+extern struct vo_driver video_out_opengl_hq;
+extern struct vo_driver video_out_opengl_old;
+extern struct vo_driver video_out_null;
+extern struct vo_driver video_out_image;
+extern struct vo_driver video_out_lavc;
+extern struct vo_driver video_out_caca;
+extern struct vo_driver video_out_direct3d;
+extern struct vo_driver video_out_direct3d_shaders;
+extern struct vo_driver video_out_corevideo;
+
+const struct vo_driver *video_out_drivers[] =
+{
+#ifdef CONFIG_DIRECT3D
+ &video_out_direct3d_shaders,
+ &video_out_direct3d,
+#endif
+#ifdef CONFIG_GL_COCOA
+ &video_out_opengl,
+ &video_out_opengl_old,
+#endif
+#ifdef CONFIG_COREVIDEO
+ &video_out_corevideo,
+#endif
+#if CONFIG_VDPAU
+ &video_out_vdpau,
+#endif
+#ifdef CONFIG_XV
+ &video_out_xv,
+#endif
+#ifdef CONFIG_GL
+#if !defined CONFIG_GL_COCOA
+ &video_out_opengl,
+ &video_out_opengl_old,
+#endif
+#endif
+#ifdef CONFIG_X11
+ &video_out_x11,
+#endif
+#ifdef CONFIG_CACA
+ &video_out_caca,
+#endif
+ &video_out_null,
+ // should not be auto-selected
+ &video_out_image,
+#ifdef CONFIG_ENCODING
+ &video_out_lavc,
+#endif
+#ifdef CONFIG_GL
+ &video_out_opengl_hq,
+#endif
+ NULL
+};
+
+
+static int vo_preinit(struct vo *vo, char *arg)
+{
+ if (vo->driver->priv_size) {
+ vo->priv = talloc_zero_size(vo, vo->driver->priv_size);
+ if (vo->driver->priv_defaults)
+ memcpy(vo->priv, vo->driver->priv_defaults, vo->driver->priv_size);
+ }
+ if (vo->driver->options) {
+ struct m_config *cfg = m_config_simple(vo->priv);
+ talloc_steal(vo->priv, cfg);
+ m_config_register_options(cfg, vo->driver->options);
+ char n[50];
+ int l = snprintf(n, sizeof(n), "vo/%s", vo->driver->info->short_name);
+ assert(l < sizeof(n));
+ int r = m_config_parse_suboptions(cfg, n, arg);
+ if (r < 0)
+ return r;
+ }
+ return vo->driver->preinit(vo, arg);
+}
+
+int vo_control(struct vo *vo, uint32_t request, void *data)
+{
+ return vo->driver->control(vo, request, data);
+}
+
+// Return -1 if driver appears not to support a draw_image interface,
+// 0 otherwise (whether the driver actually drew something or not).
+int vo_draw_image(struct vo *vo, struct mp_image *mpi, double pts)
+{
+ if (!vo->config_ok)
+ return 0;
+ if (vo->driver->buffer_frames) {
+ vo->driver->draw_image(vo, mpi, pts);
+ return 0;
+ }
+ vo->frame_loaded = true;
+ vo->next_pts = pts;
+ // Guaranteed to support at least DRAW_IMAGE later
+ if (vo->driver->is_new) {
+ vo->waiting_mpi = mpi;
+ return 0;
+ }
+ if (vo_control(vo, VOCTRL_DRAW_IMAGE, mpi) == VO_NOTIMPL)
+ return -1;
+ return 0;
+}
+
+int vo_redraw_frame(struct vo *vo)
+{
+ if (!vo->config_ok || !vo->hasframe)
+ return -1;
+ if (vo_control(vo, VOCTRL_REDRAW_FRAME, NULL) == true) {
+ vo->redrawing = true;
+ return 0;
+ }
+ return -1;
+}
+
+int vo_get_buffered_frame(struct vo *vo, bool eof)
+{
+ if (!vo->config_ok)
+ return -1;
+ if (vo->frame_loaded)
+ return 0;
+ if (!vo->driver->buffer_frames)
+ return -1;
+ vo->driver->get_buffered_frame(vo, eof);
+ return vo->frame_loaded ? 0 : -1;
+}
+
+void vo_skip_frame(struct vo *vo)
+{
+ vo_control(vo, VOCTRL_SKIPFRAME, NULL);
+ vo->frame_loaded = false;
+}
+
+int vo_draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h, int x, int y)
+{
+ return vo->driver->draw_slice(vo, src, stride, w, h, x, y);
+}
+
+void vo_new_frame_imminent(struct vo *vo)
+{
+ if (!vo->driver->is_new)
+ return;
+ if (vo->driver->buffer_frames)
+ vo_control(vo, VOCTRL_NEWFRAME, NULL);
+ else {
+ vo_control(vo, VOCTRL_DRAW_IMAGE, vo->waiting_mpi);
+ vo->waiting_mpi = NULL;
+ }
+}
+
+void vo_draw_osd(struct vo *vo, struct osd_state *osd)
+{
+ if (vo->config_ok && (vo->default_caps & VFCAP_OSD))
+ vo->driver->draw_osd(vo, osd);
+}
+
+void vo_flip_page(struct vo *vo, unsigned int pts_us, int duration)
+{
+ if (!vo->config_ok)
+ return;
+ if (!vo->redrawing) {
+ vo->frame_loaded = false;
+ vo->next_pts = MP_NOPTS_VALUE;
+ }
+ vo->want_redraw = false;
+ vo->redrawing = false;
+ if (vo->driver->flip_page_timed)
+ vo->driver->flip_page_timed(vo, pts_us, duration);
+ else
+ vo->driver->flip_page(vo);
+ vo->hasframe = true;
+}
+
+void vo_check_events(struct vo *vo)
+{
+ if (!vo->config_ok) {
+ if (vo->registered_fd != -1)
+ mp_input_rm_key_fd(vo->input_ctx, vo->registered_fd);
+ vo->registered_fd = -1;
+ return;
+ }
+ vo->driver->check_events(vo);
+}
+
+void vo_seek_reset(struct vo *vo)
+{
+ vo_control(vo, VOCTRL_RESET, NULL);
+ vo->frame_loaded = false;
+ vo->hasframe = false;
+}
+
+void vo_destroy(struct vo *vo)
+{
+ if (vo->registered_fd != -1)
+ mp_input_rm_key_fd(vo->input_ctx, vo->registered_fd);
+ vo->driver->uninit(vo);
+ talloc_free(vo);
+}
+
+void list_video_out(void)
+{
+ int i = 0;
+ mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "Available video output drivers:\n");
+ mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VIDEO_OUTPUTS\n");
+ while (video_out_drivers[i]) {
+ const vo_info_t *info = video_out_drivers[i++]->info;
+ mp_msg(MSGT_GLOBAL, MSGL_INFO,"\t%s\t%s\n", info->short_name, info->name);
+ }
+ mp_msg(MSGT_GLOBAL, MSGL_INFO,"\n");
+}
+
+static void replace_legacy_vo_name(bstr *name)
+{
+ bstr new = *name;
+ if (bstr_equals0(*name, "gl"))
+ new = bstr0("opengl");
+ if (bstr_equals0(*name, "gl3"))
+ new = bstr0("opengl-hq");
+ if (!bstr_equals(*name, new)) {
+ mp_tmsg(MSGT_CPLAYER, MSGL_ERR, "VO driver '%.*s' has been replaced "
+ "with '%.*s'!\n", BSTR_P(*name), BSTR_P(new));
+ }
+ *name = new;
+}
+
+struct vo *init_best_video_out(struct MPOpts *opts,
+ struct mp_fifo *key_fifo,
+ struct input_ctx *input_ctx,
+ struct encode_lavc_context *encode_lavc_ctx)
+{
+ char **vo_list = opts->video_driver_list;
+ int i;
+ struct vo *vo = talloc_ptrtype(NULL, vo);
+ struct vo initial_values = {
+ .opts = opts,
+ .key_fifo = key_fifo,
+ .encode_lavc_ctx = encode_lavc_ctx,
+ .input_ctx = input_ctx,
+ .event_fd = -1,
+ .registered_fd = -1,
+ };
+ // first try the preferred drivers, with their optional subdevice param:
+ if (vo_list && vo_list[0])
+ while (vo_list[0][0]) {
+ char *arg = vo_list[0];
+ bstr name = bstr0(arg);
+ char *params = strchr(arg, ':');
+ if (params) {
+ name = bstr_splice(name, 0, params - arg);
+ params++;
+ }
+ replace_legacy_vo_name(&name);
+ for (i = 0; video_out_drivers[i]; i++) {
+ const struct vo_driver *video_driver = video_out_drivers[i];
+ const vo_info_t *info = video_driver->info;
+ if (bstr_equals0(name, info->short_name)) {
+ // name matches, try it
+ *vo = initial_values;
+ vo->driver = video_driver;
+ if (!vo_preinit(vo, params))
+ return vo; // success!
+ talloc_free_children(vo);
+ }
+ }
+ // continue...
+ ++vo_list;
+ if (!(vo_list[0])) {
+ talloc_free(vo);
+ return NULL; // do NOT fallback to others
+ }
+ }
+ // now try the rest...
+ for (i = 0; video_out_drivers[i]; i++) {
+ const struct vo_driver *video_driver = video_out_drivers[i];
+ *vo = initial_values;
+ vo->driver = video_driver;
+ if (!vo_preinit(vo, NULL))
+ return vo; // success!
+ talloc_free_children(vo);
+ }
+ talloc_free(vo);
+ return NULL;
+}
+
+static int event_fd_callback(void *ctx, int fd)
+{
+ struct vo *vo = ctx;
+ vo_check_events(vo);
+ return MP_INPUT_NOTHING;
+}
+
+int vo_config(struct vo *vo, uint32_t width, uint32_t height,
+ uint32_t d_width, uint32_t d_height, uint32_t flags,
+ uint32_t format)
+{
+ struct MPOpts *opts = vo->opts;
+ panscan_init(vo);
+ aspect_save_videores(vo, width, height, d_width, d_height);
+
+ if (vo_control(vo, VOCTRL_UPDATE_SCREENINFO, NULL) == VO_TRUE) {
+ aspect(vo, &d_width, &d_height, A_NOZOOM);
+ vo->dx = (int)(opts->vo_screenwidth - d_width) / 2;
+ vo->dy = (int)(opts->vo_screenheight - d_height) / 2;
+ geometry(&vo->dx, &vo->dy, &d_width, &d_height,
+ opts->vo_screenwidth, opts->vo_screenheight);
+ geometry_xy_changed |= xinerama_screen >= 0;
+ vo->dx += xinerama_x;
+ vo->dy += xinerama_y;
+ vo->dwidth = d_width;
+ vo->dheight = d_height;
+ }
+
+ vo->default_caps = vo_control(vo, VOCTRL_QUERY_FORMAT, &format);
+
+ int ret = vo->driver->config(vo, width, height, d_width, d_height, flags,
+ format);
+ vo->config_ok = (ret == 0);
+ vo->config_count += vo->config_ok;
+ if (!vo->config_ok)
+ vo->default_caps = 0;
+ if (vo->registered_fd == -1 && vo->event_fd != -1 && vo->config_ok) {
+ mp_input_add_key_fd(vo->input_ctx, vo->event_fd, 1, event_fd_callback,
+ NULL, vo);
+ vo->registered_fd = vo->event_fd;
+ }
+ vo->frame_loaded = false;
+ vo->waiting_mpi = NULL;
+ vo->redrawing = false;
+ vo->hasframe = false;
+ return ret;
+}
+
+/**
+ * \brief lookup an integer in a table, table must have 0 as the last key
+ * \param key key to search for
+ * \result translation corresponding to key or "to" value of last mapping
+ * if not found.
+ */
+int lookup_keymap_table(const struct mp_keymap *map, int key) {
+ while (map->from && map->from != key) map++;
+ return map->to;
+}
+
+static void print_video_rect(struct vo *vo, struct mp_rect src,
+ struct mp_rect dst, struct mp_osd_res osd)
+{
+ int lv = MSGL_V;
+
+ int sw = src.x1 - src.x0, sh = src.y1 - src.y0;
+ int dw = dst.x1 - dst.x0, dh = dst.y1 - dst.y0;
+
+ mp_msg(MSGT_VO, lv, "[vo] Window size: %dx%d\n",
+ vo->dwidth, vo->dheight);
+ mp_msg(MSGT_VO, lv, "[vo] Video source: %dx%d (%dx%d)\n",
+ vo->aspdat.orgw, vo->aspdat.orgh,
+ vo->aspdat.prew, vo->aspdat.preh);
+ mp_msg(MSGT_VO, lv, "[vo] Video display: (%d, %d) %dx%d -> (%d, %d) %dx%d\n",
+ src.x0, src.y0, sw, sh, dst.x0, dst.y0, dw, dh);
+ mp_msg(MSGT_VO, lv, "[vo] Video scale: %f/%f\n",
+ (double)dw / sw, (double)dh / sh);
+ mp_msg(MSGT_VO, lv, "[vo] OSD borders: l=%d t=%d r=%d b=%d\n",
+ osd.ml, osd.mt, osd.mr, osd.mb);
+ mp_msg(MSGT_VO, lv, "[vo] Video borders: l=%d t=%d r=%d b=%d\n",
+ dst.x0, dst.y0, vo->dwidth - dst.x1, vo->dheight - dst.y1);
+}
+
+static void src_dst_split_scaling(int src_size, int dst_size,
+ int scaled_src_size, int *src_start,
+ int *src_end, int *dst_start, int *dst_end)
+{
+ if (scaled_src_size > dst_size) {
+ int border = src_size * (scaled_src_size - dst_size) / scaled_src_size;
+ // round to a multiple of 2, this is at least needed for vo_direct3d
+ // and ATI cards
+ border = (border / 2 + 1) & ~1;
+ *src_start = border;
+ *src_end = src_size - border;
+ *dst_start = 0;
+ *dst_end = dst_size;
+ } else {
+ *src_start = 0;
+ *src_end = src_size;
+ *dst_start = (dst_size - scaled_src_size) / 2;
+ *dst_end = *dst_start + scaled_src_size;
+ }
+}
+
+// Calculate the appropriate source and destination rectangle to
+// get a correctly scaled picture, including pan-scan.
+// out_src: visible part of the video
+// out_dst: area of screen covered by the video source rectangle
+// out_osd: OSD size, OSD margins, etc.
+void vo_get_src_dst_rects(struct vo *vo, struct mp_rect *out_src,
+ struct mp_rect *out_dst, struct mp_osd_res *out_osd)
+{
+ int src_w = vo->aspdat.orgw;
+ int src_h = vo->aspdat.orgh;
+ struct mp_rect dst = {0, 0, vo->dwidth, vo->dheight};
+ struct mp_rect src = {0, 0, src_w, src_h};
+ struct mp_osd_res osd = {
+ .w = vo->dwidth,
+ .h = vo->dheight,
+ .display_par = vo->monitor_par,
+ .video_par = vo->aspdat.par,
+ };
+ if (aspect_scaling()) {
+ int scaled_width = 0, scaled_height = 0;
+ aspect(vo, &scaled_width, &scaled_height, A_WINZOOM);
+ panscan_calc_windowed(vo);
+ scaled_width += vo->panscan_x;
+ scaled_height += vo->panscan_y;
+ int border_w = vo->dwidth - scaled_width;
+ int border_h = vo->dheight - scaled_height;
+ osd.ml = border_w / 2;
+ osd.mt = border_h / 2;
+ osd.mr = border_w - osd.ml;
+ osd.mb = border_h - osd.mt;
+ src_dst_split_scaling(src_w, vo->dwidth, scaled_width,
+ &src.x0, &src.x1, &dst.x0, &dst.x1);
+ src_dst_split_scaling(src_h, vo->dheight, scaled_height,
+ &src.y0, &src.y1, &dst.y0, &dst.y1);
+ }
+
+ *out_src = src;
+ *out_dst = dst;
+ *out_osd = osd;
+
+ print_video_rect(vo, src, dst, osd);
+}
+
+// Return the window title the VO should set. Always returns a null terminated
+// string. The string is valid until frontend code is invoked again. Copy it if
+// you need to keep the string for an extended period of time.
+const char *vo_get_window_title(struct vo *vo)
+{
+ if (!vo->window_title)
+ vo->window_title = talloc_strdup(vo, "");
+ return vo->window_title;
+}
+
+/**
+ * Generates a mouse movement message if those are enable and sends it
+ * to the "main" MPlayer.
+ *
+ * \param posx new x position of mouse
+ * \param posy new y position of mouse
+ */
+void vo_mouse_movement(struct vo *vo, int posx, int posy)
+{
+ char cmd_str[40];
+ if (!enable_mouse_movements)
+ return;
+ snprintf(cmd_str, sizeof(cmd_str), "set_mouse_pos %i %i", posx, posy);
+ mp_input_queue_cmd(vo->input_ctx, mp_input_parse_cmd(bstr0(cmd_str), ""));
+}
diff --git a/video/out/vo.h b/video/out/vo.h
new file mode 100644
index 0000000000..ac2ded9b3c
--- /dev/null
+++ b/video/out/vo.h
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) Aaron Holtzman - Aug 1999
+ * Strongly modified, most parts rewritten: A'rpi/ESP-team - 2000-2001
+ * (C) MPlayer developers
+ *
+ * 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_VIDEO_OUT_H
+#define MPLAYER_VIDEO_OUT_H
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include "libmpcodecs/img_format.h"
+#include "mpcommon.h"
+
+#define VO_EVENT_EXPOSE 1
+#define VO_EVENT_RESIZE 2
+#define VO_EVENT_KEYPRESS 4
+#define VO_EVENT_REINIT 8
+#define VO_EVENT_MOVE 16
+
+enum mp_voctrl {
+ /* does the device support the required format */
+ VOCTRL_QUERY_FORMAT = 1,
+ /* signal a device reset seek */
+ VOCTRL_RESET,
+ /* used to switch to fullscreen */
+ VOCTRL_FULLSCREEN,
+ /* signal a device pause */
+ VOCTRL_PAUSE,
+ /* start/resume playback */
+ VOCTRL_RESUME,
+ /* libmpcodecs direct rendering */
+ VOCTRL_GET_IMAGE,
+ VOCTRL_DRAW_IMAGE,
+ VOCTRL_GET_PANSCAN,
+ VOCTRL_SET_PANSCAN,
+ VOCTRL_SET_EQUALIZER, // struct voctrl_set_equalizer_args
+ VOCTRL_GET_EQUALIZER, // struct voctrl_get_equalizer_args
+ VOCTRL_DUPLICATE_FRAME,
+
+ VOCTRL_START_SLICE,
+
+ VOCTRL_NEWFRAME,
+ VOCTRL_SKIPFRAME,
+ VOCTRL_REDRAW_FRAME,
+
+ VOCTRL_ONTOP,
+ VOCTRL_ROOTWIN,
+ VOCTRL_BORDER,
+
+ VOCTRL_SET_DEINTERLACE,
+ VOCTRL_GET_DEINTERLACE,
+
+ VOCTRL_UPDATE_SCREENINFO,
+
+ VOCTRL_SET_YUV_COLORSPACE, // struct mp_csp_details
+ VOCTRL_GET_YUV_COLORSPACE, // struct mp_csp_details
+
+ VOCTRL_SCREENSHOT, // struct voctrl_screenshot_args
+
+ VOCTRL_SET_COMMAND_LINE, // char*
+};
+
+// VOCTRL_SET_EQUALIZER
+struct voctrl_set_equalizer_args {
+ const char *name;
+ int value;
+};
+
+// VOCTRL_GET_EQUALIZER
+struct voctrl_get_equalizer_args {
+ const char *name;
+ int *valueptr;
+};
+
+// VOCTRL_SCREENSHOT
+struct voctrl_screenshot_args {
+ // 0: Save image of the currently displayed video frame, in original
+ // resolution.
+ // 1: Save full screenshot of the window. Should contain OSD, EOSD, and the
+ // scaled video.
+ // The value of this variable can be ignored if only a single method is
+ // implemented.
+ int full_window;
+ // Will be set to a newly allocated image, that contains the screenshot.
+ // The caller has to free the pointer with free_mp_image().
+ // It is not specified whether the image data is a copy or references the
+ // image data directly.
+ // Is never NULL. (Failure has to be indicated by returning VO_FALSE.)
+ struct mp_image *out_image;
+ // Whether the VO rendered OSD/subtitles into out_image
+ bool has_osd;
+};
+
+typedef struct {
+ int x,y;
+ int w,h;
+} mp_win_t;
+
+#define VO_TRUE 1
+#define VO_FALSE 0
+#define VO_ERROR -1
+#define VO_NOTAVAIL -2
+#define VO_NOTIMPL -3
+
+#define VOFLAG_FULLSCREEN 0x01
+#define VOFLAG_MODESWITCHING 0x02
+#define VOFLAG_SWSCALE 0x04
+#define VOFLAG_FLIPPING 0x08
+#define VOFLAG_HIDDEN 0x10 //< Use to create a hidden window
+#define VOFLAG_STEREO 0x20 //< Use to create a stereo-capable window
+#define VOFLAG_GL_DEBUG 0x40 // Hint to request debug OpenGL context
+
+typedef struct vo_info_s
+{
+ /* driver name ("Matrox Millennium G200/G400" */
+ const char *name;
+ /* short name (for config strings) ("vdpau") */
+ const char *short_name;
+ /* author ("Aaron Holtzman <aholtzma@ess.engr.uvic.ca>") */
+ const char *author;
+ /* any additional comments */
+ const char *comment;
+} vo_info_t;
+
+struct vo;
+struct osd_state;
+struct mp_image;
+
+struct vo_driver {
+ // Driver uses new API
+ bool is_new;
+ // Driver buffers or adds (deinterlace) frames and will keep track
+ // of pts values itself
+ bool buffer_frames;
+
+ const vo_info_t *info;
+ /*
+ * Preinitializes driver (real INITIALIZATION)
+ * arg - currently it's vo_subdevice
+ * returns: zero on successful initialization, non-zero on error.
+ */
+ int (*preinit)(struct vo *vo, const char *arg);
+ /*
+ * Initialize (means CONFIGURE) the display driver.
+ * params:
+ * width,height: image source size
+ * d_width,d_height: size of the requested window size, just a hint
+ * fullscreen: flag, 0=windowd 1=fullscreen, just a hint
+ * title: window title, if available
+ * format: fourcc of pixel format
+ * returns : zero on successful initialization, non-zero on error.
+ */
+ int (*config)(struct vo *vo, uint32_t width, uint32_t height,
+ uint32_t d_width, uint32_t d_height, uint32_t fullscreen,
+ uint32_t format);
+
+ /*
+ * Control interface
+ */
+ int (*control)(struct vo *vo, uint32_t request, void *data);
+
+ void (*draw_image)(struct vo *vo, struct mp_image *mpi, double pts);
+
+ /*
+ * Get extra frames from the VO, such as those added by VDPAU
+ * deinterlace. Preparing the next such frame if any could be done
+ * automatically by the VO after a previous flip_page(), but having
+ * it as a separate step seems to allow making code more robust.
+ */
+ void (*get_buffered_frame)(struct vo *vo, bool eof);
+
+ /*
+ * Draw a planar YUV slice to the buffer:
+ * params:
+ * src[3] = source image planes (Y,U,V)
+ * stride[3] = source image planes line widths (in bytes)
+ * w,h = width*height of area to be copied (in Y pixels)
+ * x,y = position at the destination image (in Y pixels)
+ */
+ int (*draw_slice)(struct vo *vo, uint8_t *src[], int stride[], int w,
+ int h, int x, int y);
+
+ /*
+ * Draws OSD to the screen buffer
+ */
+ void (*draw_osd)(struct vo *vo, struct osd_state *osd);
+
+ /*
+ * Blit/Flip buffer to the screen. Must be called after each frame!
+ */
+ void (*flip_page)(struct vo *vo);
+ void (*flip_page_timed)(struct vo *vo, unsigned int pts_us, int duration);
+
+ /*
+ * This func is called after every frames to handle keyboard and
+ * other events. It's called in PAUSE mode too!
+ */
+ void (*check_events)(struct vo *vo);
+
+ /*
+ * Closes driver. Should restore the original state of the system.
+ */
+ void (*uninit)(struct vo *vo);
+
+ // Size of private struct for automatic allocation (0 doesn't allocate)
+ int priv_size;
+
+ // If not NULL, it's copied into the newly allocated private struct.
+ const void *priv_defaults;
+
+ // List of options to parse into priv struct (requires privsize to be set)
+ const struct m_option *options;
+};
+
+struct vo {
+ int config_ok; // Last config call was successful?
+ int config_count; // Total number of successful config calls
+ int default_caps; // query_format() result for configured video format
+
+ bool frame_loaded; // Is there a next frame the VO could flip to?
+ struct mp_image *waiting_mpi;
+ double next_pts; // pts value of the next frame if any
+ double next_pts2; // optional pts of frame after that
+ bool want_redraw; // visible frame wrong (window resize), needs refresh
+ bool redrawing; // between redrawing frame and flipping it
+ bool hasframe; // >= 1 frame has been drawn, so redraw is possible
+
+ double flip_queue_offset; // queue flip events at most this much in advance
+
+ const struct vo_driver *driver;
+ void *priv;
+ struct MPOpts *opts;
+ struct vo_x11_state *x11;
+ struct vo_w32_state *w32;
+ struct vo_cocoa_state *cocoa;
+ struct mp_fifo *key_fifo;
+ struct encode_lavc_context *encode_lavc_ctx;
+ struct input_ctx *input_ctx;
+ int event_fd; // check_events() should be called when this has input
+ int registered_fd; // set to event_fd when registered in input system
+
+ // requested position/resolution (usually window position/window size)
+ int dx;
+ int dy;
+ int dwidth;
+ int dheight;
+
+ int panscan_x;
+ int panscan_y;
+ float panscan_amount;
+ float monitor_par;
+ struct aspect_data {
+ int orgw; // real width
+ int orgh; // real height
+ int prew; // prescaled width
+ int preh; // prescaled height
+ float par; // pixel aspect ratio out of orgw/orgh and prew/preh
+ int scrw; // horizontal resolution
+ int scrh; // vertical resolution
+ float asp;
+ } aspdat;
+
+ char *window_title;
+};
+
+struct vo *init_best_video_out(struct MPOpts *opts,
+ struct mp_fifo *key_fifo,
+ struct input_ctx *input_ctx,
+ struct encode_lavc_context *encode_lavc_ctx);
+int vo_config(struct vo *vo, uint32_t width, uint32_t height,
+ uint32_t d_width, uint32_t d_height, uint32_t flags,
+ uint32_t format);
+void list_video_out(void);
+
+int vo_control(struct vo *vo, uint32_t request, void *data);
+int vo_draw_image(struct vo *vo, struct mp_image *mpi, double pts);
+int vo_redraw_frame(struct vo *vo);
+int vo_get_buffered_frame(struct vo *vo, bool eof);
+void vo_skip_frame(struct vo *vo);
+int vo_draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h, int x, int y);
+void vo_new_frame_imminent(struct vo *vo);
+void vo_draw_osd(struct vo *vo, struct osd_state *osd);
+void vo_flip_page(struct vo *vo, unsigned int pts_us, int duration);
+void vo_check_events(struct vo *vo);
+void vo_seek_reset(struct vo *vo);
+void vo_destroy(struct vo *vo);
+
+const char *vo_get_window_title(struct vo *vo);
+
+// NULL terminated array of all drivers
+extern const struct vo_driver *video_out_drivers[];
+
+extern int xinerama_screen;
+extern int xinerama_x;
+extern int xinerama_y;
+
+extern int vo_grabpointer;
+extern int vo_vsync;
+extern int vo_fs;
+extern int vo_fsmode;
+extern float vo_panscan;
+extern int vo_refresh_rate;
+extern int vo_keepaspect;
+extern int vo_rootwin;
+extern int vo_border;
+
+extern int vo_nomouse_input;
+extern int enable_mouse_movements;
+
+extern int vo_pts;
+extern float vo_fps;
+
+extern int vo_colorkey;
+
+extern int64_t WinID;
+
+struct mp_keymap {
+ int from;
+ int to;
+};
+int lookup_keymap_table(const struct mp_keymap *map, int key);
+
+void vo_mouse_movement(struct vo *vo, int posx, int posy);
+
+struct mp_osd_res;
+void vo_get_src_dst_rects(struct vo *vo, struct mp_rect *out_src,
+ struct mp_rect *out_dst, struct mp_osd_res *out_osd);
+
+static inline int aspect_scaling(void)
+{
+ return vo_keepaspect || vo_fs;
+}
+
+#endif /* MPLAYER_VIDEO_OUT_H */
diff --git a/video/out/vo_caca.c b/video/out/vo_caca.c
new file mode 100644
index 0000000000..2d998bf91d
--- /dev/null
+++ b/video/out/vo_caca.c
@@ -0,0 +1,395 @@
+/*
+ * video output driver for libcaca
+ *
+ * by Pigeon <pigeon@pigeond.net>
+ *
+ * Some functions/codes/ideas are from x11 and aalib vo
+ *
+ * TODO: support draw_alpha?
+ *
+ * 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 <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <assert.h>
+#include <caca.h>
+
+#include "config.h"
+#include "video_out.h"
+#include "sub/sub.h"
+#include "libmpcodecs/mp_image.h"
+#include "libmpcodecs/vfcap.h"
+
+#include "input/keycodes.h"
+#include "input/input.h"
+#include "mp_msg.h"
+#include "mp_fifo.h"
+
+/* caca stuff */
+static caca_canvas_t *canvas;
+static caca_display_t *display;
+static caca_dither_t *dither = NULL;
+static const char *dither_antialias = "default";
+static const char *dither_charset = "default";
+static const char *dither_color = "default";
+static const char *dither_algo = "none";
+
+/* image infos */
+static int image_format;
+static int image_width;
+static int image_height;
+
+static int screen_w, screen_h;
+
+/* We want 24bpp always for now */
+static unsigned int bpp = 24;
+static unsigned int depth = 3;
+static unsigned int rmask = 0xff0000;
+static unsigned int gmask = 0x00ff00;
+static unsigned int bmask = 0x0000ff;
+static unsigned int amask = 0;
+
+#define MESSAGE_SIZE 512
+#define MESSAGE_DURATION 5
+
+static time_t stoposd = 0;
+static int showosdmessage = 0;
+static char osdmessagetext[MESSAGE_SIZE];
+static char posbar[MESSAGE_SIZE];
+
+static int osdx = 0, osdy = 0;
+static int posbary = 2;
+
+static void osdmessage(int duration, const char *fmt, ...)
+{
+ /* for outputting a centered string at the window bottom for a while */
+ va_list ar;
+ char m[MESSAGE_SIZE];
+
+ va_start(ar, fmt);
+ vsprintf(m, fmt, ar);
+ va_end(ar);
+ strcpy(osdmessagetext, m);
+
+ showosdmessage = 1;
+ stoposd = time(NULL) + duration;
+ osdx = (screen_w - strlen(osdmessagetext)) / 2;
+ posbar[0] = '\0';
+}
+
+static void osdpercent(int duration, int min, int max, int val,
+ const char *desc, const char *unit)
+{
+ /* prints a bar for setting values */
+ float step;
+ int where, i;
+
+ step = (float)screen_w / (float)(max - min);
+ where = (val - min) * step;
+ osdmessage(duration, "%s: %i%s", desc, val, unit);
+ posbar[0] = '|';
+ posbar[screen_w - 1] = '|';
+
+ for (i = 0; i < screen_w; i++) {
+ if (i == where)
+ posbar[i] = '#';
+ else
+ posbar[i] = '-';
+ }
+
+ if (where != 0)
+ posbar[0] = '|';
+
+ if (where != (screen_w - 1))
+ posbar[screen_w - 1] = '|';
+
+ posbar[screen_w] = '\0';
+}
+
+static int resize(void)
+{
+ screen_w = caca_get_canvas_width(canvas);
+ screen_h = caca_get_canvas_height(canvas);
+
+ caca_free_dither(dither);
+
+ dither = caca_create_dither(bpp, image_width, image_height,
+ depth * image_width,
+ rmask, gmask, bmask, amask);
+ if (dither == NULL) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "vo_caca: caca_create_dither failed!\n");
+ return ENOSYS;
+ }
+
+ /* Default libcaca features */
+ caca_set_dither_antialias(dither, dither_antialias);
+ caca_set_dither_charset(dither, dither_charset);
+ caca_set_dither_color(dither, dither_color);
+ caca_set_dither_algorithm(dither, dither_algo);
+
+ return 0;
+}
+
+static int config(struct vo *vo, uint32_t width, uint32_t height,
+ uint32_t d_width, uint32_t d_height, uint32_t flags,
+ uint32_t format)
+{
+ image_height = height;
+ image_width = width;
+ image_format = format;
+
+ showosdmessage = 0;
+ posbar[0] = '\0';
+
+ return resize();
+}
+
+static uint32_t draw_image(struct vo *vo, mp_image_t *mpi)
+{
+ assert(mpi->stride[0] == image_width * 3);
+ caca_dither_bitmap(canvas, 0, 0, screen_w, screen_h, dither,
+ mpi->planes[0]);
+ return true;
+}
+
+static int draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h,
+ int x, int y)
+{
+ return 0;
+}
+
+static void flip_page(struct vo *vo)
+{
+ if (showosdmessage) {
+ if (time(NULL) >= stoposd) {
+ showosdmessage = 0;
+ if (*posbar)
+ posbar[0] = '\0';
+ } else {
+ caca_put_str(canvas, osdx, osdy, osdmessagetext);
+ if (*posbar)
+ caca_put_str(canvas, 0, posbary, posbar);
+ }
+ }
+
+ caca_refresh_display(display);
+}
+
+static void set_next_str(const char * const *list, const char **str,
+ const char **msg)
+{
+ int ind;
+ for (ind = 0; list[ind]; ind += 2) {
+ if (strcmp(list[ind], *str) == 0) {
+ if (list[ind + 2] == NULL)
+ ind = -2;
+ *str = list[ind + 2];
+ *msg = list[ind + 3];
+ return;
+ }
+ }
+
+ *str = list[0];
+ *msg = list[1];
+}
+
+static const struct mp_keymap keysym_map[] = {
+ {CACA_KEY_RETURN, KEY_ENTER}, {CACA_KEY_ESCAPE, KEY_ESC},
+ {CACA_KEY_UP, KEY_DOWN}, {CACA_KEY_DOWN, KEY_DOWN},
+ {CACA_KEY_LEFT, KEY_LEFT}, {CACA_KEY_RIGHT, KEY_RIGHT},
+ {CACA_KEY_PAGEUP, KEY_PAGE_UP}, {CACA_KEY_PAGEDOWN, KEY_PAGE_DOWN},
+ {CACA_KEY_HOME, KEY_HOME}, {CACA_KEY_END, KEY_END},
+ {CACA_KEY_INSERT, KEY_INSERT}, {CACA_KEY_DELETE, KEY_DELETE},
+ {CACA_KEY_BACKSPACE, KEY_BACKSPACE}, {CACA_KEY_TAB, KEY_TAB},
+ {CACA_KEY_PAUSE, KEY_PAUSE},
+ {CACA_KEY_F1, KEY_F+1}, {CACA_KEY_F2, KEY_F+2},
+ {CACA_KEY_F3, KEY_F+3}, {CACA_KEY_F4, KEY_F+4},
+ {CACA_KEY_F5, KEY_F+5}, {CACA_KEY_F6, KEY_F+6},
+ {CACA_KEY_F7, KEY_F+7}, {CACA_KEY_F8, KEY_F+8},
+ {CACA_KEY_F9, KEY_F+9}, {CACA_KEY_F10, KEY_F+10},
+ {CACA_KEY_F11, KEY_F+11}, {CACA_KEY_F12, KEY_F+12},
+ {CACA_KEY_F13, KEY_F+13}, {CACA_KEY_F14, KEY_F+14},
+ {CACA_KEY_F15, KEY_F+15},
+ {0, 0}
+};
+
+static void check_events(struct vo *vo)
+{
+ caca_event_t cev;
+ while (caca_get_event(display, CACA_EVENT_ANY, &cev, 0)) {
+
+ switch (cev.type) {
+ case CACA_EVENT_RESIZE:
+ caca_refresh_display(display);
+ resize();
+ break;
+ case CACA_EVENT_QUIT:
+ mplayer_put_key(vo->key_fifo, KEY_CLOSE_WIN);
+ break;
+ case CACA_EVENT_MOUSE_MOTION:
+ vo_mouse_movement(vo, cev.data.mouse.x, cev.data.mouse.y);
+ break;
+ case CACA_EVENT_MOUSE_PRESS:
+ if (!vo_nomouse_input)
+ mplayer_put_key(vo->key_fifo,
+ (MOUSE_BTN0 + cev.data.mouse.button - 1) | MP_KEY_DOWN);
+ break;
+ case CACA_EVENT_MOUSE_RELEASE:
+ if (!vo_nomouse_input)
+ mplayer_put_key(vo->key_fifo,
+ MOUSE_BTN0 + cev.data.mouse.button - 1);
+ break;
+ case CACA_EVENT_KEY_PRESS:
+ {
+ int key = cev.data.key.ch;
+ int mpkey = lookup_keymap_table(keysym_map, key);
+ const char *msg_name;
+
+ if (mpkey)
+ mplayer_put_key(vo->key_fifo, mpkey);
+ else
+ switch (key) {
+ case 'd':
+ case 'D':
+ /* Toggle dithering algorithm */
+ set_next_str(caca_get_dither_algorithm_list(dither),
+ &dither_algo, &msg_name);
+ caca_set_dither_algorithm(dither, dither_algo);
+ osdmessage(MESSAGE_DURATION, "Using %s", msg_name);
+ break;
+
+ case 'a':
+ case 'A':
+ /* Toggle antialiasing method */
+ set_next_str(caca_get_dither_antialias_list(dither),
+ &dither_antialias, &msg_name);
+ caca_set_dither_antialias(dither, dither_antialias);
+ osdmessage(MESSAGE_DURATION, "Using %s", msg_name);
+ break;
+
+ case 'h':
+ case 'H':
+ /* Toggle charset method */
+ set_next_str(caca_get_dither_charset_list(dither),
+ &dither_charset, &msg_name);
+ caca_set_dither_charset(dither, dither_charset);
+ osdmessage(MESSAGE_DURATION, "Using %s", msg_name);
+ break;
+
+ case 'c':
+ case 'C':
+ /* Toggle color method */
+ set_next_str(caca_get_dither_color_list(dither),
+ &dither_color, &msg_name);
+ caca_set_dither_color(dither, dither_color);
+ osdmessage(MESSAGE_DURATION, "Using %s", msg_name);
+ break;
+
+ default:
+ if (key <= 255)
+ mplayer_put_key(vo->key_fifo, key);
+ break;
+ }
+ }
+ }
+ }
+}
+
+static void uninit(struct vo *vo)
+{
+ caca_free_dither(dither);
+ dither = NULL;
+ caca_free_display(display);
+ caca_free_canvas(canvas);
+}
+
+static void draw_osd(struct vo *vo, struct osd_state *osd)
+{
+ if (osd->progbar_type != -1)
+ osdpercent(MESSAGE_DURATION, 0, 255, osd->progbar_value,
+ sub_osd_names[osd->progbar_type], "");
+}
+
+static int preinit(struct vo *vo, const char *arg)
+{
+ if (arg) {
+ mp_msg(MSGT_VO, MSGL_ERR, "vo_caca: Unknown subdevice: %s\n", arg);
+ return ENOSYS;
+ }
+
+ canvas = caca_create_canvas(0, 0);
+ if (canvas == NULL) {
+ mp_msg(MSGT_VO, MSGL_ERR, "vo_caca: failed to create canvas\n");
+ return ENOSYS;
+ }
+
+ display = caca_create_display(canvas);
+
+ if (display == NULL) {
+ mp_msg(MSGT_VO, MSGL_ERR, "vo_caca: failed to create display\n");
+ caca_free_canvas(canvas);
+ return ENOSYS;
+ }
+
+ caca_set_display_title(display, "mpv");
+
+ return 0;
+}
+
+static int query_format(uint32_t format)
+{
+ if (format == IMGFMT_BGR24)
+ return VFCAP_OSD | VFCAP_CSP_SUPPORTED;
+
+ return 0;
+}
+
+static int control(struct vo *vo, uint32_t request, void *data)
+{
+ switch (request) {
+ case VOCTRL_QUERY_FORMAT:
+ return query_format(*((uint32_t *)data));
+ case VOCTRL_DRAW_IMAGE:
+ return draw_image(vo, data);
+ default:
+ return VO_NOTIMPL;
+ }
+}
+
+const struct vo_driver video_out_caca = {
+ .is_new = false,
+ .info = &(const vo_info_t) {
+ "libcaca",
+ "caca",
+ "Pigeon <pigeon@pigeond.net>",
+ ""
+ },
+ .preinit = preinit,
+ .config = config,
+ .control = control,
+ .draw_slice = draw_slice,
+ .draw_osd = draw_osd,
+ .flip_page = flip_page,
+ .check_events = check_events,
+ .uninit = uninit,
+};
diff --git a/video/out/vo_corevideo.h b/video/out/vo_corevideo.h
new file mode 100644
index 0000000000..cfb86621bc
--- /dev/null
+++ b/video/out/vo_corevideo.h
@@ -0,0 +1,28 @@
+/*
+ * CoreVideo video output driver
+ *
+ * Copyright (c) 2005 Nicolas Plourde <nicolasplourde@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.
+ */
+
+#ifndef MPLAYER_VO_COREVIDEO_H
+#define MPLAYER_VO_COREVIDEO_H
+
+#import <QuartzCore/QuartzCore.h>
+
+#endif /* MPLAYER_VO_COREVIDEO_H */
diff --git a/video/out/vo_corevideo.m b/video/out/vo_corevideo.m
new file mode 100644
index 0000000000..5116ab653c
--- /dev/null
+++ b/video/out/vo_corevideo.m
@@ -0,0 +1,457 @@
+/*
+ * CoreVideo video output driver
+ * Copyright (c) 2005 Nicolas Plourde <nicolasplourde@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 <assert.h>
+
+#import "vo_corevideo.h"
+
+// mplayer includes
+#import "fastmemcpy.h"
+#import "talloc.h"
+#import "video_out.h"
+#import "aspect.h"
+#import "sub/sub.h"
+#import "subopt-helper.h"
+
+#import "csputils.h"
+#import "libmpcodecs/vfcap.h"
+#import "libmpcodecs/mp_image.h"
+
+#import "gl_common.h"
+#import "gl_osd.h"
+#import "cocoa_common.h"
+
+struct quad {
+ GLfloat lowerLeft[2];
+ GLfloat lowerRight[2];
+ GLfloat upperRight[2];
+ GLfloat upperLeft[2];
+};
+
+struct priv {
+ MPGLContext *mpglctx;
+ OSType pixelFormat;
+ unsigned int image_width;
+ unsigned int image_height;
+ struct mp_csp_details colorspace;
+ int ass_border_x, ass_border_y;
+
+ CVPixelBufferRef pixelBuffer;
+ CVOpenGLTextureCacheRef textureCache;
+ CVOpenGLTextureRef texture;
+ struct quad *quad;
+
+ struct mpgl_osd *osd;
+};
+
+static void resize(struct vo *vo, int width, int height)
+{
+ struct priv *p = vo->priv;
+ GL *gl = p->mpglctx->gl;
+
+ gl->Viewport(0, 0, width, height);
+ gl->MatrixMode(GL_PROJECTION);
+ gl->LoadIdentity();
+ p->ass_border_x = p->ass_border_y = 0;
+ if (aspect_scaling()) {
+ int new_w, new_h;
+ GLdouble scale_x, scale_y;
+
+ aspect(vo, &new_w, &new_h, A_WINZOOM);
+ panscan_calc_windowed(vo);
+ new_w += vo->panscan_x;
+ new_h += vo->panscan_y;
+ scale_x = (GLdouble)new_w / (GLdouble)width;
+ scale_y = (GLdouble)new_h / (GLdouble)height;
+ gl->Scaled(scale_x, scale_y, 1);
+ p->ass_border_x = (vo->dwidth - new_w) / 2;
+ p->ass_border_y = (vo->dheight - new_h) / 2;
+ }
+
+ gl->Ortho(0, p->image_width, p->image_height, 0, -1.0, 1.0);
+ gl->MatrixMode(GL_MODELVIEW);
+ gl->LoadIdentity();
+
+ gl->Clear(GL_COLOR_BUFFER_BIT);
+ vo->want_redraw = true;
+}
+
+static int init_gl(struct vo *vo, uint32_t d_width, uint32_t d_height)
+{
+ struct priv *p = vo->priv;
+ GL *gl = p->mpglctx->gl;
+
+ const char *vendor = gl->GetString(GL_VENDOR);
+ const char *version = gl->GetString(GL_VERSION);
+ const char *renderer = gl->GetString(GL_RENDERER);
+
+ mp_msg(MSGT_VO, MSGL_V, "[vo_corevideo] Running on OpenGL '%s' by '%s',"
+ " version '%s'\n", renderer, vendor, version);
+
+ gl->Disable(GL_BLEND);
+ gl->Disable(GL_DEPTH_TEST);
+ gl->DepthMask(GL_FALSE);
+ gl->Disable(GL_CULL_FACE);
+ gl->Enable(GL_TEXTURE_2D);
+ gl->DrawBuffer(GL_BACK);
+ gl->TexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ if (!p->osd)
+ p->osd = mpgl_osd_init(gl, true);
+
+ resize(vo, d_width, d_height);
+
+ gl->ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ gl->Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ if (gl->SwapInterval)
+ gl->SwapInterval(1);
+ return 1;
+}
+
+static void release_cv_entities(struct vo *vo) {
+ struct priv *p = vo->priv;
+ CVPixelBufferRelease(p->pixelBuffer);
+ p->pixelBuffer = NULL;
+ CVOpenGLTextureRelease(p->texture);
+ p->texture = NULL;
+ CVOpenGLTextureCacheRelease(p->textureCache);
+ p->textureCache = NULL;
+}
+
+static int config(struct vo *vo, uint32_t width, uint32_t height,
+ uint32_t d_width, uint32_t d_height, uint32_t flags,
+ uint32_t format)
+{
+ struct priv *p = vo->priv;
+ release_cv_entities(vo);
+ p->image_width = width;
+ p->image_height = height;
+
+ int mpgl_caps = MPGL_CAP_GL_LEGACY;
+ if (!mpgl_create_window(p->mpglctx, mpgl_caps, d_width, d_height, flags))
+ return -1;
+
+ init_gl(vo, vo->dwidth, vo->dheight);
+
+ return 0;
+}
+
+static void check_events(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ int e = p->mpglctx->check_events(vo);
+ if (e & VO_EVENT_RESIZE)
+ resize(vo, vo->dwidth, vo->dheight);
+}
+
+static void prepare_texture(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ struct quad *q = p->quad;
+ CVReturn error;
+
+ CVOpenGLTextureRelease(p->texture);
+ error = CVOpenGLTextureCacheCreateTextureFromImage(NULL,
+ p->textureCache, p->pixelBuffer, 0, &p->texture);
+ if(error != kCVReturnSuccess)
+ mp_msg(MSGT_VO, MSGL_ERR,"[vo_corevideo] Failed to create OpenGL"
+ " texture(%d)\n", error);
+
+ CVOpenGLTextureGetCleanTexCoords(p->texture, q->lowerLeft, q->lowerRight,
+ q->upperRight, q->upperLeft);
+}
+
+static void do_render(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ struct quad *q = p->quad;
+ GL *gl = p->mpglctx->gl;
+ prepare_texture(vo);
+
+ float x0 = 0;
+ float y0 = 0;
+ float w = p->image_width;
+ float h = p->image_height;
+
+ // vertically flips the image
+ y0 += h;
+ h = -h;
+
+ float xm = x0 + w;
+ float ym = y0 + h;
+
+ gl->Enable(CVOpenGLTextureGetTarget(p->texture));
+ gl->BindTexture(
+ CVOpenGLTextureGetTarget(p->texture),
+ CVOpenGLTextureGetName(p->texture));
+
+ gl->Begin(GL_QUADS);
+ gl->TexCoord2fv(q->lowerLeft); gl->Vertex2f(x0, y0);
+ gl->TexCoord2fv(q->upperLeft); gl->Vertex2f(x0, ym);
+ gl->TexCoord2fv(q->upperRight); gl->Vertex2f(xm, ym);
+ gl->TexCoord2fv(q->lowerRight); gl->Vertex2f(xm, y0);
+ gl->End();
+
+ gl->Disable(CVOpenGLTextureGetTarget(p->texture));
+}
+
+static void flip_page(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ p->mpglctx->swapGlBuffers(p->mpglctx);
+ p->mpglctx->gl->Clear(GL_COLOR_BUFFER_BIT);
+}
+
+static uint32_t draw_image(struct vo *vo, mp_image_t *mpi)
+{
+ struct priv *p = vo->priv;
+ CVReturn error;
+
+ if (!p->textureCache || !p->pixelBuffer) {
+ error = CVOpenGLTextureCacheCreate(NULL, 0, vo_cocoa_cgl_context(vo),
+ vo_cocoa_cgl_pixel_format(vo), 0, &p->textureCache);
+ if(error != kCVReturnSuccess)
+ mp_msg(MSGT_VO, MSGL_ERR,"[vo_corevideo] Failed to create OpenGL"
+ " texture Cache(%d)\n", error);
+
+ error = CVPixelBufferCreateWithBytes(NULL, mpi->width, mpi->height,
+ p->pixelFormat, mpi->planes[0], mpi->width * mpi->bpp / 8,
+ NULL, NULL, NULL, &p->pixelBuffer);
+ if(error != kCVReturnSuccess)
+ mp_msg(MSGT_VO, MSGL_ERR,"[vo_corevideo] Failed to create Pixel"
+ "Buffer(%d)\n", error);
+ }
+
+ do_render(vo);
+ return VO_TRUE;
+}
+
+static int query_format(struct vo *vo, uint32_t format)
+{
+ struct priv *p = vo->priv;
+ const int flags = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW |
+ VFCAP_OSD | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN |
+ VOCAP_NOSLICES;
+ switch (format) {
+ case IMGFMT_YUY2:
+ p->pixelFormat = kYUVSPixelFormat;
+ return flags;
+
+ case IMGFMT_RGB24:
+ p->pixelFormat = k24RGBPixelFormat;
+ return flags;
+
+ case IMGFMT_ARGB:
+ p->pixelFormat = k32ARGBPixelFormat;
+ return flags;
+
+ case IMGFMT_BGRA:
+ p->pixelFormat = k32BGRAPixelFormat;
+ return flags;
+ }
+ return 0;
+}
+
+static void uninit(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ if (p->osd)
+ mpgl_osd_destroy(p->osd);
+ release_cv_entities(vo);
+ mpgl_uninit(p->mpglctx);
+}
+
+
+static int preinit(struct vo *vo, const char *arg)
+{
+ struct priv *p = vo->priv;
+
+ *p = (struct priv) {
+ .mpglctx = mpgl_init(GLTYPE_COCOA, vo),
+ .colorspace = MP_CSP_DETAILS_DEFAULTS,
+ .quad = talloc_ptrtype(p, p->quad),
+ };
+
+ return 0;
+}
+
+static void draw_osd(struct vo *vo, struct osd_state *osd)
+{
+ struct priv *p = vo->priv;
+ GL *gl = p->mpglctx->gl;
+ assert(p->osd);
+
+ gl->MatrixMode(GL_PROJECTION);
+ gl->PushMatrix();
+ gl->LoadIdentity();
+ gl->Ortho(0, vo->dwidth, vo->dheight, 0, -1, 1);
+
+ struct mp_osd_res res = {
+ .w = vo->dwidth,
+ .h = vo->dheight,
+ .display_par = vo->monitor_par,
+ .video_par = vo->aspdat.par,
+ };
+
+ if (aspect_scaling()) {
+ res.ml = res.mr = p->ass_border_x;
+ res.mt = res.mb = p->ass_border_y;
+ }
+
+ mpgl_osd_draw_legacy(p->osd, osd, res);
+
+ gl->PopMatrix();
+}
+
+static CFStringRef get_cv_csp_matrix(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ switch (p->colorspace.format) {
+ case MP_CSP_BT_601:
+ return kCVImageBufferYCbCrMatrix_ITU_R_601_4;
+ case MP_CSP_BT_709:
+ return kCVImageBufferYCbCrMatrix_ITU_R_709_2;
+ case MP_CSP_SMPTE_240M:
+ return kCVImageBufferYCbCrMatrix_SMPTE_240M_1995;
+ }
+ return kCVImageBufferYCbCrMatrix_ITU_R_601_4;
+}
+
+static void set_yuv_colorspace(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ CVBufferSetAttachment(p->pixelBuffer,
+ kCVImageBufferYCbCrMatrixKey, get_cv_csp_matrix(vo),
+ kCVAttachmentMode_ShouldPropagate);
+ vo->want_redraw = true;
+}
+
+static int get_image_fmt(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ switch (p->pixelFormat) {
+ case kYUVSPixelFormat: return IMGFMT_YUY2;
+ case k24RGBPixelFormat: return IMGFMT_RGB24;
+ case k32ARGBPixelFormat: return IMGFMT_ARGB;
+ case k32BGRAPixelFormat: return IMGFMT_BGRA;
+ }
+ mp_msg(MSGT_VO, MSGL_ERR, "[vo_corevideo] Failed to convert pixel format. "
+ "Please contact the developers. PixelFormat: %d\n", p->pixelFormat);
+ return -1;
+}
+
+static mp_image_t *get_screenshot(struct vo *vo)
+{
+ int img_fmt = get_image_fmt(vo);
+ if (img_fmt < 0) return NULL;
+
+ struct priv *p = vo->priv;
+ void *base = CVPixelBufferGetBaseAddress(p->pixelBuffer);
+
+ size_t width = CVPixelBufferGetWidth(p->pixelBuffer);
+ size_t height = CVPixelBufferGetHeight(p->pixelBuffer);
+ size_t stride = CVPixelBufferGetBytesPerRow(p->pixelBuffer);
+ size_t image_size = stride * height;
+
+ mp_image_t *image = alloc_mpi(width, height, img_fmt);
+ memcpy(image->planes[0], base, image_size);
+ image->stride[0] = stride;
+
+ image->display_w = vo->aspdat.prew;
+ image->display_h = vo->aspdat.preh;
+
+ mp_image_set_colorspace_details(image, &p->colorspace);
+
+ return image;
+}
+
+static int control(struct vo *vo, uint32_t request, void *data)
+{
+ struct priv *p = vo->priv;
+ switch (request) {
+ case VOCTRL_DRAW_IMAGE:
+ return draw_image(vo, data);
+ case VOCTRL_QUERY_FORMAT:
+ return query_format(vo, *(uint32_t*)data);
+ case VOCTRL_ONTOP:
+ p->mpglctx->ontop(vo);
+ return VO_TRUE;
+ case VOCTRL_PAUSE:
+ if (!p->mpglctx->pause)
+ break;
+ p->mpglctx->pause(vo);
+ return VO_TRUE;
+ case VOCTRL_RESUME:
+ if (!p->mpglctx->resume)
+ break;
+ p->mpglctx->resume(vo);
+ return VO_TRUE;
+ case VOCTRL_FULLSCREEN:
+ p->mpglctx->fullscreen(vo);
+ resize(vo, vo->dwidth, vo->dheight);
+ return VO_TRUE;
+ case VOCTRL_GET_PANSCAN:
+ return VO_TRUE;
+ case VOCTRL_SET_PANSCAN:
+ resize(vo, vo->dwidth, vo->dheight);
+ return VO_TRUE;
+ case VOCTRL_UPDATE_SCREENINFO:
+ p->mpglctx->update_xinerama_info(vo);
+ return VO_TRUE;
+ case VOCTRL_REDRAW_FRAME:
+ do_render(vo);
+ return VO_TRUE;
+ case VOCTRL_SET_YUV_COLORSPACE:
+ p->colorspace.format = ((struct mp_csp_details *)data)->format;
+ set_yuv_colorspace(vo);
+ return VO_TRUE;
+ case VOCTRL_GET_YUV_COLORSPACE:
+ *(struct mp_csp_details *)data = p->colorspace;
+ return VO_TRUE;
+ case VOCTRL_SCREENSHOT: {
+ struct voctrl_screenshot_args *args = data;
+ if (args->full_window)
+ args->out_image = glGetWindowScreenshot(p->mpglctx->gl);
+ else
+ args->out_image = get_screenshot(vo);
+ return VO_TRUE;
+ }
+ }
+ return VO_NOTIMPL;
+}
+
+const struct vo_driver video_out_corevideo = {
+ .is_new = true,
+ .info = &(const vo_info_t) {
+ "Mac OS X Core Video",
+ "corevideo",
+ "Nicolas Plourde <nicolas.plourde@gmail.com> and others",
+ ""
+ },
+ .preinit = preinit,
+ .config = config,
+ .control = control,
+ .draw_osd = draw_osd,
+ .flip_page = flip_page,
+ .check_events = check_events,
+ .uninit = uninit,
+ .priv_size = sizeof(struct priv),
+};
diff --git a/video/out/vo_direct3d.c b/video/out/vo_direct3d.c
new file mode 100644
index 0000000000..294a101ffe
--- /dev/null
+++ b/video/out/vo_direct3d.c
@@ -0,0 +1,2101 @@
+/*
+ * Copyright (c) 2008 Georgi Petrov (gogothebee) <gogothebee@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 <windows.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <d3d9.h>
+#include "config.h"
+#include "options.h"
+#include "subopt-helper.h"
+#include "talloc.h"
+#include "video_out.h"
+#include "libmpcodecs/vfcap.h"
+#include "csputils.h"
+#include "libmpcodecs/mp_image.h"
+#include "libmpcodecs/img_format.h"
+#include "fastmemcpy.h"
+#include "mp_msg.h"
+#include "aspect.h"
+#include "w32_common.h"
+#include "libavutil/common.h"
+#include "sub/sub.h"
+#include "bitmap_packer.h"
+
+// shaders generated by fxc.exe from d3d_shader_yuv.hlsl
+#include "d3d_shader_yuv.h"
+#include "d3d_shader_yuv_2ch.h"
+
+
+// TODO: beg someone to add this (there is already IMGFMT_Y8)
+// equals MAKEFOURCC('Y', '1', '6', ' ')
+#define IMGFMT_Y16 0x20363159
+#define IMGFMT_A8Y8 MAKEFOURCC('A', '8', 'Y', '8')
+
+#define IMGFMT_IS_Y(x) ((x) == IMGFMT_Y8 || (x) == IMGFMT_Y16 || (x) == IMGFMT_A8Y8)
+#define IMGFMT_Y_DEPTH(x) ((x) == IMGFMT_Y8 ? 8 : 16)
+
+#define DEVTYPE D3DDEVTYPE_HAL
+//#define DEVTYPE D3DDEVTYPE_REF
+
+#define D3DFVF_OSD_VERTEX (D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_DIFFUSE)
+
+typedef struct {
+ float x, y, z;
+ D3DCOLOR color;
+ float tu, tv;
+} vertex_osd;
+
+#define D3DFVF_VIDEO_VERTEX (D3DFVF_XYZ | D3DFVF_TEX3)
+
+typedef struct {
+ float x, y, z;
+ // pairs of texture coordinates for up to 3 planes
+ float t[3][2];
+} vertex_video;
+
+
+struct d3dtex {
+ // user-requested size
+ int w, h;
+ // allocated texture size
+ int tex_w, tex_h;
+ // D3DPOOL_SYSTEMMEM (or others) texture:
+ // - can be locked in order to write (and even read) data
+ // - can _not_ (probably) be used as texture for rendering
+ // This is always non-NULL if d3dtex_allocate succeeds.
+ IDirect3DTexture9 *system;
+ // D3DPOOL_DEFAULT texture:
+ // - can't be locked (Probably.)
+ // - must be used for rendering
+ // This will be NULL on systems with device_texture_sys != 0.
+ IDirect3DTexture9 *device;
+};
+
+struct texplane {
+ int bytes_per_pixel;
+ int bits_per_pixel;
+ // chroma shifts
+ // e.g. get the plane's width in pixels with (priv->src_width >> shift_x)
+ int shift_x, shift_y;
+ D3DFORMAT d3d_format;
+ struct d3dtex texture;
+ // temporary locking during uploading the frame (e.g. for draw_slice)
+ D3DLOCKED_RECT locked_rect;
+ // value used to clear the image with memset (YUV chroma planes do not use
+ // the value 0 for this)
+ uint8_t clearval;
+};
+
+struct osdpart {
+ enum sub_bitmap_format format;
+ int bitmap_id, bitmap_pos_id;
+ struct d3dtex texture;
+ int num_vertices;
+ vertex_osd *vertices;
+ struct bitmap_packer *packer;
+};
+
+/* Global variables "priv" structure. I try to keep their count low.
+ */
+typedef struct d3d_priv {
+ int opt_prefer_stretchrect;
+ int opt_disable_textures;
+ int opt_disable_stretchrect;
+ int opt_disable_shaders;
+ int opt_only_8bit;
+ int opt_disable_osd;
+ int opt_disable_texture_align;
+ // debugging
+ int opt_force_power_of_2;
+ int opt_texture_memory;
+ int opt_swap_discard;
+ int opt_exact_backbuffer;
+ int opt_16bit_textures;
+
+ struct vo *vo;
+
+ int is_clear_needed; /**< 1 = Clear the backbuffer before StretchRect
+ 0 = (default) Don't clear it */
+ D3DLOCKED_RECT locked_rect; /**< The locked offscreen surface */
+ RECT fs_movie_rect; /**< Rect (upscaled) of the movie when displayed
+ in fullscreen */
+ RECT fs_panscan_rect; /**< PanScan source surface cropping in
+ fullscreen */
+ int src_width; /**< Source (movie) width */
+ int src_height; /**< Source (movie) heigth */
+ struct mp_osd_res osd_res;
+ int image_format; /**< mplayer image format */
+ bool use_textures; /**< use 3D texture rendering, instead of
+ StretchRect */
+ bool use_shaders; /**< use shader for YUV color conversion
+ (or possibly for RGB video equalizers) */
+ bool use_2ch_hack; /**< 2 byte YUV formats use 2 channel hack */
+
+ int plane_count;
+ struct texplane planes[3];
+
+ IDirect3DPixelShader9 *pixel_shader;
+ const BYTE *pixel_shader_data;
+
+ D3DFORMAT movie_src_fmt; /**< Movie colorspace format (depends on
+ the movie's codec) */
+ D3DFORMAT desktop_fmt; /**< Desktop (screen) colorspace format.
+ Usually XRGB */
+
+ HANDLE d3d9_dll; /**< d3d9 Library HANDLE */
+ IDirect3D9 * (WINAPI *pDirect3DCreate9)(UINT); /**< pointer to Direct3DCreate9 function */
+
+ LPDIRECT3D9 d3d_handle; /**< Direct3D Handle */
+ LPDIRECT3DDEVICE9 d3d_device; /**< The Direct3D Adapter */
+ bool d3d_in_scene; /**< BeginScene was called, EndScene not */
+ IDirect3DSurface9 *d3d_surface; /**< Offscreen Direct3D Surface. MPlayer
+ renders inside it. Uses colorspace
+ priv->movie_src_fmt */
+ IDirect3DSurface9 *d3d_backbuf; /**< Video card's back buffer (used to
+ display next frame) */
+ int cur_backbuf_width; /**< Current backbuffer width */
+ int cur_backbuf_height; /**< Current backbuffer height */
+ int device_caps_power2_only; /**< 1 = texture sizes have to be power 2
+ 0 = texture sizes can be anything */
+ int device_caps_square_only; /**< 1 = textures have to be square
+ 0 = textures do not have to be square */
+ int device_texture_sys; /**< 1 = device can texture from system memory
+ 0 = device requires shadow */
+ int max_texture_width; /**< from the device capabilities */
+ int max_texture_height; /**< from the device capabilities */
+
+ D3DMATRIX d3d_colormatrix;
+ float d3d_depth_vector[4];
+ struct mp_csp_details colorspace;
+ struct mp_csp_equalizer video_eq;
+
+ struct osdpart *osd[MAX_OSD_PARTS];
+} d3d_priv;
+
+struct fmt_entry {
+ const unsigned int mplayer_fmt; /**< Given by MPlayer */
+ const D3DFORMAT fourcc; /**< Required by D3D's test function */
+};
+
+/* Map table from reported MPlayer format to the required
+ fourcc. This is needed to perform the format query. */
+
+static const struct fmt_entry fmt_table[] = {
+ // planar YUV
+ {IMGFMT_YV12, MAKEFOURCC('Y','V','1','2')},
+ {IMGFMT_I420, MAKEFOURCC('I','4','2','0')},
+ {IMGFMT_IYUV, MAKEFOURCC('I','Y','U','V')},
+ {IMGFMT_YVU9, MAKEFOURCC('Y','V','U','9')},
+ // packed YUV
+ {IMGFMT_YUY2, D3DFMT_YUY2},
+ {IMGFMT_UYVY, D3DFMT_UYVY},
+ // packed RGB
+ {IMGFMT_BGR32, D3DFMT_X8R8G8B8},
+ {IMGFMT_RGB32, D3DFMT_X8B8G8R8},
+ {IMGFMT_BGR24, D3DFMT_R8G8B8}, //untested
+ {IMGFMT_BGR16, D3DFMT_R5G6B5},
+ {IMGFMT_BGR15, D3DFMT_X1R5G5B5},
+ {IMGFMT_BGR8 , D3DFMT_R3G3B2}, //untested
+ // grayscale (can be considered both packed and planar)
+ {IMGFMT_Y8, D3DFMT_L8},
+ {IMGFMT_Y16, D3DFMT_L16},
+ {IMGFMT_A8Y8, D3DFMT_A8L8},
+ {0},
+};
+
+static const D3DFORMAT osd_fmt_table[SUBBITMAP_COUNT] = {
+ [SUBBITMAP_LIBASS] = D3DFMT_A8,
+ [SUBBITMAP_RGBA] = D3DFMT_A8R8G8B8,
+};
+static const bool osd_fmt_supported[SUBBITMAP_COUNT] = {
+ [SUBBITMAP_LIBASS] = true,
+ [SUBBITMAP_RGBA] = true,
+};
+
+
+static void update_colorspace(d3d_priv *priv);
+static void d3d_clear_video_textures(d3d_priv *priv);
+static bool resize_d3d(d3d_priv *priv);
+static uint32_t d3d_draw_frame(d3d_priv *priv);
+static int draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h,
+ int x, int y);
+static void uninit(struct vo *vo);
+static void flip_page(struct vo *vo);
+static mp_image_t *get_screenshot(d3d_priv *priv);
+static mp_image_t *get_window_screenshot(d3d_priv *priv);
+
+
+static void d3d_matrix_identity(D3DMATRIX *m)
+{
+ memset(m, 0, sizeof(D3DMATRIX));
+ m->_11 = m->_22 = m->_33 = m->_44 = 1.0f;
+}
+
+static void d3d_matrix_ortho(D3DMATRIX *m, float left, float right,
+ float bottom, float top)
+{
+ d3d_matrix_identity(m);
+ m->_11 = 2.0f / (right - left);
+ m->_22 = 2.0f / (top - bottom);
+ m->_33 = 1.0f;
+ m->_41 = -(right + left) / (right - left);
+ m->_42 = -(top + bottom) / (top - bottom);
+ m->_43 = 0;
+ m->_44 = 1.0f;
+}
+
+/****************************************************************************
+ * *
+ * *
+ * *
+ * Direct3D specific implementation functions *
+ * *
+ * *
+ * *
+ ****************************************************************************/
+
+static bool d3d_begin_scene(d3d_priv *priv)
+{
+ if (!priv->d3d_in_scene) {
+ if (FAILED(IDirect3DDevice9_BeginScene(priv->d3d_device))) {
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>BeginScene failed.\n");
+ return false;
+ }
+ priv->d3d_in_scene = true;
+ }
+ return true;
+}
+
+/** @brief Calculate scaled fullscreen movie rectangle with
+ * preserved aspect ratio.
+ */
+static void calc_fs_rect(d3d_priv *priv)
+{
+ struct mp_rect src_rect;
+ struct mp_rect dst_rect;
+ vo_get_src_dst_rects(priv->vo, &src_rect, &dst_rect, &priv->osd_res);
+
+ priv->fs_movie_rect.left = dst_rect.x0;
+ priv->fs_movie_rect.right = dst_rect.x1;
+ priv->fs_movie_rect.top = dst_rect.y0;
+ priv->fs_movie_rect.bottom = dst_rect.y1;
+ priv->fs_panscan_rect.left = src_rect.x0;
+ priv->fs_panscan_rect.right = src_rect.x1;
+ priv->fs_panscan_rect.top = src_rect.y0;
+ priv->fs_panscan_rect.bottom = src_rect.y1;
+
+ mp_msg(MSGT_VO, MSGL_V,
+ "<vo_direct3d>Video rectangle: t: %ld, l: %ld, r: %ld, b:%ld\n",
+ priv->fs_movie_rect.top, priv->fs_movie_rect.left,
+ priv->fs_movie_rect.right, priv->fs_movie_rect.bottom);
+
+ /* The backbuffer should be cleared before next StretchRect. This is
+ * necessary because our new draw area could be smaller than the
+ * previous one used by StretchRect and without it, leftovers from the
+ * previous frame will be left. */
+ priv->is_clear_needed = 1;
+}
+
+// Adjust the texture size *width/*height to fit the requirements of the D3D
+// device. The texture size is only increased.
+static void d3d_fix_texture_size(d3d_priv *priv, int *width, int *height)
+{
+ int tex_width = *width;
+ int tex_height = *height;
+
+ // avoid nasty special cases with 0-sized textures and texture sizes
+ tex_width = FFMAX(tex_width, 1);
+ tex_height = FFMAX(tex_height, 1);
+
+ if (priv->device_caps_power2_only) {
+ tex_width = 1;
+ tex_height = 1;
+ while (tex_width < *width) tex_width <<= 1;
+ while (tex_height < *height) tex_height <<= 1;
+ }
+ if (priv->device_caps_square_only)
+ /* device only supports square textures */
+ tex_width = tex_height = FFMAX(tex_width, tex_height);
+ // better round up to a multiple of 16
+ if (!priv->opt_disable_texture_align) {
+ tex_width = (tex_width + 15) & ~15;
+ tex_height = (tex_height + 15) & ~15;
+ }
+
+ *width = tex_width;
+ *height = tex_height;
+}
+
+static void d3dtex_release(d3d_priv *priv, struct d3dtex *tex)
+{
+ if (tex->system)
+ IDirect3DTexture9_Release(tex->system);
+ tex->system = NULL;
+
+ if (tex->device)
+ IDirect3DTexture9_Release(tex->device);
+ tex->device = NULL;
+
+ tex->tex_w = tex->tex_h = 0;
+}
+
+static bool d3dtex_allocate(d3d_priv *priv, struct d3dtex *tex, D3DFORMAT fmt,
+ int w, int h)
+{
+ d3dtex_release(priv, tex);
+
+ tex->w = w;
+ tex->h = h;
+
+ int tw = w, th = h;
+ d3d_fix_texture_size(priv, &tw, &th);
+
+ int memtype = D3DPOOL_SYSTEMMEM;
+ switch (priv->opt_texture_memory) {
+ case 1: memtype = D3DPOOL_MANAGED; break;
+ case 2: memtype = D3DPOOL_DEFAULT; break;
+ }
+
+ if (FAILED(IDirect3DDevice9_CreateTexture(priv->d3d_device, tw, th, 1,
+ D3DUSAGE_DYNAMIC, fmt, memtype, &tex->system, NULL)))
+ {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "<vo_direct3d>Allocating %dx%d texture in system RAM failed.\n",
+ w, h);
+ goto error_exit;
+ }
+
+ if (!priv->device_texture_sys && !priv->opt_texture_memory) {
+ if (FAILED(IDirect3DDevice9_CreateTexture(priv->d3d_device, tw, th, 1,
+ D3DUSAGE_DYNAMIC, fmt, D3DPOOL_DEFAULT, &tex->device, NULL)))
+ {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "<vo_direct3d>Allocating %dx%d texture in video RAM failed.\n",
+ w, h);
+ goto error_exit;
+ }
+ }
+
+ tex->tex_w = tw;
+ tex->tex_h = th;
+
+ return true;
+
+error_exit:
+ d3dtex_release(priv, tex);
+ return false;
+}
+
+static IDirect3DBaseTexture9 *d3dtex_get_render_texture(d3d_priv *priv,
+ struct d3dtex *tex)
+{
+ return (IDirect3DBaseTexture9 *)(tex->device ? tex->device : tex->system);
+}
+
+// Copy system texture contents to device texture.
+static bool d3dtex_update(d3d_priv *priv, struct d3dtex *tex)
+{
+ if (!tex->device)
+ return true;
+ return !FAILED(IDirect3DDevice9_UpdateTexture(priv->d3d_device,
+ (IDirect3DBaseTexture9 *)tex->system,
+ (IDirect3DBaseTexture9 *)tex->device));
+}
+
+static void d3d_unlock_video_objects(d3d_priv *priv)
+{
+ bool any_failed = false;
+
+ if (priv->locked_rect.pBits) {
+ if (FAILED(IDirect3DSurface9_UnlockRect(priv->d3d_surface)))
+ any_failed = true;
+ }
+ priv->locked_rect.pBits = NULL;
+
+ for (int n = 0; n < priv->plane_count; n++) {
+ struct texplane *plane = &priv->planes[n];
+ if (plane->locked_rect.pBits) {
+ if (FAILED(IDirect3DTexture9_UnlockRect(plane->texture.system, 0)))
+ any_failed = true;
+ }
+ plane->locked_rect.pBits = NULL;
+ }
+
+ if (any_failed) {
+ mp_msg(MSGT_VO, MSGL_V,
+ "<vo_direct3d>Unlocking video objects failed.\n");
+ }
+}
+
+// Free video surface/textures, shaders, etc.
+static void d3d_destroy_video_objects(d3d_priv *priv)
+{
+ d3d_unlock_video_objects(priv);
+
+ if (priv->d3d_surface)
+ IDirect3DSurface9_Release(priv->d3d_surface);
+ priv->d3d_surface = NULL;
+
+ for (int n = 0; n < priv->plane_count; n++) {
+ d3dtex_release(priv, &priv->planes[n].texture);
+ }
+
+ if (priv->pixel_shader)
+ IDirect3DPixelShader9_Release(priv->pixel_shader);
+ priv->pixel_shader = NULL;
+}
+
+/** @brief Destroy D3D Offscreen and Backbuffer surfaces.
+ */
+static void destroy_d3d_surfaces(d3d_priv *priv)
+{
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>destroy_d3d_surfaces called.\n");
+
+ d3d_destroy_video_objects(priv);
+
+ for (int n = 0; n < MAX_OSD_PARTS; n++) {
+ struct osdpart *osd = priv->osd[n];
+ d3dtex_release(priv, &osd->texture);
+ osd->bitmap_id = osd->bitmap_pos_id = -1;
+ }
+
+ if (priv->d3d_backbuf)
+ IDirect3DSurface9_Release(priv->d3d_backbuf);
+ priv->d3d_backbuf = NULL;
+
+ priv->d3d_in_scene = false;
+}
+
+// Allocate video surface or textures, and create shaders if needed.
+static bool d3d_configure_video_objects(d3d_priv *priv)
+{
+ int n;
+ bool need_clear = false;
+
+ assert(priv->image_format != 0);
+
+ if (priv->use_textures) {
+ for (n = 0; n < priv->plane_count; n++) {
+ struct texplane *plane = &priv->planes[n];
+
+ if (!plane->texture.system) {
+ if (!d3dtex_allocate(priv,
+ &plane->texture,
+ plane->d3d_format,
+ priv->src_width >> plane->shift_x,
+ priv->src_height >> plane->shift_y))
+ {
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Allocating plane %d"
+ " failed.\n", n);
+ return false;
+ }
+
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Allocated plane %d:"
+ " %d bit, shift=%d/%d size=%d/%d (%d/%d).\n", n,
+ plane->bits_per_pixel,
+ plane->shift_x, plane->shift_y,
+ plane->texture.w, plane->texture.h,
+ plane->texture.tex_w, plane->texture.tex_h);
+
+ need_clear = true;
+ }
+ }
+
+ if (need_clear)
+ d3d_clear_video_textures(priv);
+
+ if (priv->pixel_shader_data) {
+ if (!priv->pixel_shader &&
+ FAILED(IDirect3DDevice9_CreatePixelShader(priv->d3d_device,
+ (DWORD *)priv->pixel_shader_data, &priv->pixel_shader)))
+ {
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Failed to create "
+ "YUV conversion pixel shader.\n");
+ return false;
+ }
+ }
+
+ } else {
+
+ if (!priv->d3d_surface &&
+ FAILED(IDirect3DDevice9_CreateOffscreenPlainSurface(
+ priv->d3d_device, priv->src_width, priv->src_height,
+ priv->movie_src_fmt, D3DPOOL_DEFAULT, &priv->d3d_surface, NULL)))
+ {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "<vo_direct3d>Allocating offscreen surface failed.\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool d3d_lock_video_textures(d3d_priv *priv)
+{
+ for (int n = 0; n < priv->plane_count; n++) {
+ struct texplane *plane = &priv->planes[n];
+
+ if (!plane->locked_rect.pBits) {
+ if (FAILED(IDirect3DTexture9_LockRect(plane->texture.system, 0,
+ &plane->locked_rect, NULL, 0)))
+ {
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Texture lock failure.\n");
+ d3d_unlock_video_objects(priv);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+static void d3d_clear_video_textures(d3d_priv *priv)
+{
+ if (!d3d_lock_video_textures(priv))
+ return;
+
+ for (int n = 0; n < priv->plane_count; n++) {
+ struct texplane *plane = &priv->planes[n];
+ memset(plane->locked_rect.pBits, plane->clearval,
+ plane->locked_rect.Pitch * plane->texture.tex_h);
+ }
+
+ d3d_unlock_video_objects(priv);
+}
+
+// Recreate and initialize D3D objects if necessary. The amount of work that
+// needs to be done can be quite different: it could be that full initialization
+// is required, or that some objects need to be created, or that nothing is
+// done.
+static bool create_d3d_surfaces(d3d_priv *priv)
+{
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>create_d3d_surfaces called.\n");
+
+ if (!priv->d3d_backbuf &&
+ FAILED(IDirect3DDevice9_GetBackBuffer(priv->d3d_device, 0, 0,
+ D3DBACKBUFFER_TYPE_MONO,
+ &priv->d3d_backbuf))) {
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Allocating backbuffer failed.\n");
+ return 0;
+ }
+
+ if (!d3d_configure_video_objects(priv))
+ return 0;
+
+ /* setup default renderstate */
+ IDirect3DDevice9_SetRenderState(priv->d3d_device,
+ D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
+ IDirect3DDevice9_SetRenderState(priv->d3d_device,
+ D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
+ IDirect3DDevice9_SetRenderState(priv->d3d_device,
+ D3DRS_ALPHAFUNC, D3DCMP_GREATER);
+ IDirect3DDevice9_SetRenderState(priv->d3d_device,
+ D3DRS_ALPHAREF, (DWORD)0x0);
+ IDirect3DDevice9_SetRenderState(priv->d3d_device,
+ D3DRS_LIGHTING, FALSE);
+
+ // we use up to 3 samplers for up to 3 YUV planes
+ for (int n = 0; n < 3; n++) {
+ IDirect3DDevice9_SetSamplerState(priv->d3d_device, n, D3DSAMP_MINFILTER,
+ D3DTEXF_LINEAR);
+ IDirect3DDevice9_SetSamplerState(priv->d3d_device, n, D3DSAMP_MAGFILTER,
+ D3DTEXF_LINEAR);
+ IDirect3DDevice9_SetSamplerState(priv->d3d_device, n, D3DSAMP_ADDRESSU,
+ D3DTADDRESS_CLAMP);
+ IDirect3DDevice9_SetSamplerState(priv->d3d_device, n, D3DSAMP_ADDRESSV,
+ D3DTADDRESS_CLAMP);
+ }
+
+ return 1;
+}
+
+static bool init_d3d(d3d_priv *priv)
+{
+ D3DDISPLAYMODE disp_mode;
+ D3DCAPS9 disp_caps;
+ DWORD texture_caps;
+ DWORD dev_caps;
+
+ priv->d3d_handle = priv->pDirect3DCreate9(D3D_SDK_VERSION);
+ if (!priv->d3d_handle) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "<vo_direct3d>Initializing Direct3D failed.\n");
+ return false;
+ }
+
+ if (FAILED(IDirect3D9_GetAdapterDisplayMode(priv->d3d_handle,
+ D3DADAPTER_DEFAULT,
+ &disp_mode))) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "<vo_direct3d>Reading display mode failed.\n");
+ return false;
+ }
+
+ priv->desktop_fmt = disp_mode.Format;
+ priv->cur_backbuf_width = disp_mode.Width;
+ priv->cur_backbuf_height = disp_mode.Height;
+
+ mp_msg(MSGT_VO, MSGL_V,
+ "<vo_direct3d>Setting backbuffer dimensions to (%dx%d).\n",
+ disp_mode.Width, disp_mode.Height);
+
+ if (FAILED(IDirect3D9_GetDeviceCaps(priv->d3d_handle,
+ D3DADAPTER_DEFAULT,
+ DEVTYPE,
+ &disp_caps)))
+ {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "<vo_direct3d>Reading display capabilities failed.\n");
+ return false;
+ }
+
+ /* Store relevant information reguarding caps of device */
+ texture_caps = disp_caps.TextureCaps;
+ dev_caps = disp_caps.DevCaps;
+ priv->device_caps_power2_only = (texture_caps & D3DPTEXTURECAPS_POW2) &&
+ !(texture_caps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL);
+ priv->device_caps_square_only = texture_caps & D3DPTEXTURECAPS_SQUAREONLY;
+ priv->device_texture_sys = dev_caps & D3DDEVCAPS_TEXTURESYSTEMMEMORY;
+ priv->max_texture_width = disp_caps.MaxTextureWidth;
+ priv->max_texture_height = disp_caps.MaxTextureHeight;
+
+ if (priv->opt_force_power_of_2)
+ priv->device_caps_power2_only = 1;
+
+ mp_msg(MSGT_VO, MSGL_V,
+ "<vo_direct3d>device_caps_power2_only %d, device_caps_square_only %d\n"
+ "<vo_direct3d>device_texture_sys %d\n"
+ "<vo_direct3d>max_texture_width %d, max_texture_height %d\n",
+ priv->device_caps_power2_only, priv->device_caps_square_only,
+ priv->device_texture_sys, priv->max_texture_width,
+ priv->max_texture_height);
+
+ return true;
+}
+
+/** @brief Fill D3D Presentation parameters
+ */
+static void fill_d3d_presentparams(d3d_priv *priv,
+ D3DPRESENT_PARAMETERS *present_params)
+{
+ /* Prepare Direct3D initialization parameters. */
+ memset(present_params, 0, sizeof(D3DPRESENT_PARAMETERS));
+ present_params->Windowed = TRUE;
+ present_params->SwapEffect =
+ priv->opt_swap_discard ? D3DSWAPEFFECT_DISCARD : D3DSWAPEFFECT_COPY;
+ present_params->Flags = D3DPRESENTFLAG_VIDEO;
+ present_params->hDeviceWindow = priv->vo->w32->window;
+ present_params->BackBufferWidth = priv->cur_backbuf_width;
+ present_params->BackBufferHeight = priv->cur_backbuf_height;
+ present_params->MultiSampleType = D3DMULTISAMPLE_NONE;
+ present_params->PresentationInterval = D3DPRESENT_INTERVAL_ONE;
+ present_params->BackBufferFormat = priv->desktop_fmt;
+ present_params->BackBufferCount = 1;
+ present_params->EnableAutoDepthStencil = FALSE;
+}
+
+
+// Create a new backbuffer. Create or Reset the D3D device.
+static bool change_d3d_backbuffer(d3d_priv *priv)
+{
+ D3DPRESENT_PARAMETERS present_params;
+
+ int window_w = priv->vo->dwidth;
+ int window_h = priv->vo->dheight;
+
+ /* Grow the backbuffer in the required dimension. */
+ if (window_w > priv->cur_backbuf_width)
+ priv->cur_backbuf_width = window_w;
+
+ if (window_h > priv->cur_backbuf_height)
+ priv->cur_backbuf_height = window_h;
+
+ if (priv->opt_exact_backbuffer) {
+ priv->cur_backbuf_width = window_w;
+ priv->cur_backbuf_height = window_h;
+ }
+
+ /* The grown backbuffer dimensions are ready and fill_d3d_presentparams
+ * will use them, so we can reset the device.
+ */
+ fill_d3d_presentparams(priv, &present_params);
+
+ if (!priv->d3d_device) {
+ if (FAILED(IDirect3D9_CreateDevice(priv->d3d_handle,
+ D3DADAPTER_DEFAULT,
+ DEVTYPE, priv->vo->w32->window,
+ D3DCREATE_SOFTWARE_VERTEXPROCESSING
+ | D3DCREATE_FPU_PRESERVE,
+ &present_params, &priv->d3d_device)))
+ {
+ mp_msg(MSGT_VO, MSGL_V,
+ "<vo_direct3d>Creating Direct3D device failed.\n");
+ return 0;
+ }
+ } else {
+ if (FAILED(IDirect3DDevice9_Reset(priv->d3d_device, &present_params))) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "<vo_direct3d>Reseting Direct3D device failed.\n");
+ return 0;
+ }
+ }
+
+ mp_msg(MSGT_VO, MSGL_V,
+ "<vo_direct3d>New backbuffer (%dx%d), VO (%dx%d)\n",
+ present_params.BackBufferWidth, present_params.BackBufferHeight,
+ window_w, window_h);
+
+ return 1;
+}
+
+/** @brief Reconfigure the whole Direct3D. Called only
+ * when the video adapter becomes uncooperative. ("Lost" devices)
+ * @return 1 on success, 0 on failure
+ */
+static int reconfigure_d3d(d3d_priv *priv)
+{
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>reconfigure_d3d called.\n");
+
+ destroy_d3d_surfaces(priv);
+
+ if (priv->d3d_device)
+ IDirect3DDevice9_Release(priv->d3d_device);
+ priv->d3d_device = NULL;
+
+ // Force complete destruction of the D3D state.
+ // Note: this step could be omitted. The resize_d3d call below would detect
+ // that d3d_device is NULL, and would properly recreate it. I'm not sure why
+ // the following code to release and recreate the d3d_handle exists.
+ if (priv->d3d_handle)
+ IDirect3D9_Release(priv->d3d_handle);
+ priv->d3d_handle = NULL;
+ if (!init_d3d(priv))
+ return 0;
+
+ // Proper re-initialization.
+ if (!resize_d3d(priv))
+ return 0;
+
+ return 1;
+}
+
+// Resize Direct3D context on window resize.
+// This function also is called when major initializations need to be done.
+static bool resize_d3d(d3d_priv *priv)
+{
+ D3DVIEWPORT9 vp = {0, 0, priv->vo->dwidth, priv->vo->dheight, 0, 1};
+
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>resize_d3d called.\n");
+
+ /* Make sure that backbuffer is large enough to accomodate the new
+ viewport dimensions. Grow it if necessary. */
+
+ bool backbuf_resize = priv->vo->dwidth > priv->cur_backbuf_width ||
+ priv->vo->dheight > priv->cur_backbuf_height;
+
+ if (priv->opt_exact_backbuffer) {
+ backbuf_resize = priv->vo->dwidth != priv->cur_backbuf_width ||
+ priv->vo->dheight != priv->cur_backbuf_height;
+ }
+
+ if (backbuf_resize || !priv->d3d_device)
+ {
+ destroy_d3d_surfaces(priv);
+ if (!change_d3d_backbuffer(priv))
+ return 0;
+ }
+
+ if (!priv->d3d_device)
+ return 1;
+
+ if (!create_d3d_surfaces(priv))
+ return 0;
+
+ if (FAILED(IDirect3DDevice9_SetViewport(priv->d3d_device, &vp))) {
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Setting viewport failed.\n");
+ return 0;
+ }
+
+ // so that screen coordinates map to D3D ones
+ D3DMATRIX view;
+ d3d_matrix_ortho(&view, 0.5f, vp.Width + 0.5f, vp.Height + 0.5f, 0.5f);
+ IDirect3DDevice9_SetTransform(priv->d3d_device, D3DTS_VIEW, &view);
+
+ calc_fs_rect(priv);
+
+ priv->vo->want_redraw = true;
+
+ return 1;
+}
+
+/** @brief Uninitialize Direct3D and close the window.
+ */
+static void uninit_d3d(d3d_priv *priv)
+{
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>uninit_d3d called.\n");
+
+ destroy_d3d_surfaces(priv);
+
+ if (priv->d3d_device)
+ IDirect3DDevice9_Release(priv->d3d_device);
+ priv->d3d_device = NULL;
+
+ if (priv->d3d_handle) {
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Stopping Direct3D.\n");
+ IDirect3D9_Release(priv->d3d_handle);
+ }
+ priv->d3d_handle = NULL;
+}
+
+static uint32_t d3d_upload_and_render_frame_texture(d3d_priv *priv,
+ mp_image_t *mpi)
+{
+ if (!(mpi->flags & MP_IMGFLAG_DRAW_CALLBACK))
+ draw_slice(priv->vo, mpi->planes, mpi->stride, mpi->w, mpi->h, 0, 0);
+
+ d3d_unlock_video_objects(priv);
+
+ for (int n = 0; n < priv->plane_count; n++) {
+ d3dtex_update(priv, &priv->planes[n].texture);
+ }
+
+ return d3d_draw_frame(priv);
+}
+
+/** @brief Render a frame on the screen.
+ * @param mpi mpi structure with the decoded frame inside
+ * @return VO_TRUE on success, VO_ERROR on failure
+ */
+static uint32_t d3d_upload_and_render_frame(d3d_priv *priv, mp_image_t *mpi)
+{
+ if (!priv->d3d_device)
+ return VO_TRUE;
+
+ if (priv->use_textures)
+ return d3d_upload_and_render_frame_texture(priv, mpi);
+
+ if (mpi->flags & MP_IMGFLAG_DRAW_CALLBACK)
+ goto skip_upload;
+
+ if (mpi->flags & MP_IMGFLAG_PLANAR) { /* Copy a planar frame. */
+ draw_slice(priv->vo, mpi->planes, mpi->stride, mpi->w, mpi->h, 0, 0);
+ goto skip_upload;
+ }
+
+ /* If we're here, then we should lock the rect and copy a packed frame */
+ if (!priv->locked_rect.pBits) {
+ if (FAILED(IDirect3DSurface9_LockRect(priv->d3d_surface,
+ &priv->locked_rect, NULL, 0))) {
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Surface lock failed.\n");
+ return VO_ERROR;
+ }
+ }
+
+ memcpy_pic(priv->locked_rect.pBits, mpi->planes[0], mpi->stride[0],
+ mpi->height, priv->locked_rect.Pitch, mpi->stride[0]);
+
+skip_upload:
+ d3d_unlock_video_objects(priv);
+
+ return d3d_draw_frame(priv);
+}
+
+static uint32_t d3d_draw_frame(d3d_priv *priv)
+{
+ int n;
+
+ if (!priv->d3d_device)
+ return VO_TRUE;
+
+ if (!d3d_begin_scene(priv))
+ return VO_ERROR;
+
+ if (priv->is_clear_needed || priv->opt_swap_discard) {
+ IDirect3DDevice9_Clear(priv->d3d_device, 0, NULL,
+ D3DCLEAR_TARGET, 0, 0, 0);
+ priv->is_clear_needed = 0;
+ }
+
+ if (priv->use_textures) {
+
+ for (n = 0; n < priv->plane_count; n++) {
+ IDirect3DDevice9_SetTexture(priv->d3d_device, n,
+ d3dtex_get_render_texture(priv, &priv->planes[n].texture));
+ }
+
+ RECT rm = priv->fs_movie_rect;
+ RECT rs = priv->fs_panscan_rect;
+
+ vertex_video vb[] = {
+ { rm.left, rm.top, 0.0f},
+ { rm.right, rm.top, 0.0f},
+ { rm.left, rm.bottom, 0.0f},
+ { rm.right, rm.bottom, 0.0f}
+ };
+
+ float texc[4][2] = {
+ { rs.left, rs.top},
+ { rs.right, rs.top},
+ { rs.left, rs.bottom},
+ { rs.right, rs.bottom}
+ };
+
+ for (n = 0; n < priv->plane_count; n++) {
+ float s_x = (1.0f / (1 << priv->planes[n].shift_x))
+ / priv->planes[n].texture.tex_w;
+ float s_y = (1.0f / (1 << priv->planes[n].shift_y))
+ / priv->planes[n].texture.tex_h;
+ for (int i = 0; i < 4; i++) {
+ vb[i].t[n][0] = texc[i][0] * s_x;
+ vb[i].t[n][1] = texc[i][1] * s_y;
+ }
+ }
+
+ if (priv->pixel_shader) {
+ IDirect3DDevice9_SetPixelShader(priv->d3d_device, priv->pixel_shader);
+ IDirect3DDevice9_SetPixelShaderConstantF(priv->d3d_device, 0,
+ &priv->d3d_colormatrix._11,
+ 4);
+ IDirect3DDevice9_SetPixelShaderConstantF(priv->d3d_device, 5,
+ priv->d3d_depth_vector,
+ 1);
+ }
+
+ IDirect3DDevice9_SetFVF(priv->d3d_device, D3DFVF_VIDEO_VERTEX);
+ IDirect3DDevice9_DrawPrimitiveUP(priv->d3d_device, D3DPT_TRIANGLESTRIP,
+ 2, &vb[0], sizeof(vertex_video));
+
+ IDirect3DDevice9_SetPixelShader(priv->d3d_device, NULL);
+
+ for (n = 0; n < priv->plane_count; n++) {
+ IDirect3DDevice9_SetTexture(priv->d3d_device, n, NULL);
+ }
+
+ } else {
+ if (FAILED(IDirect3DDevice9_StretchRect(priv->d3d_device,
+ priv->d3d_surface,
+ &priv->fs_panscan_rect,
+ priv->d3d_backbuf,
+ &priv->fs_movie_rect,
+ D3DTEXF_LINEAR))) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "<vo_direct3d>Copying frame to the backbuffer failed.\n");
+ return VO_ERROR;
+ }
+ }
+
+ return VO_TRUE;
+}
+
+// Return the high byte of the value that represents white in chroma (U/V)
+static int get_chroma_clear_val(int bit_depth)
+{
+ return 1 << (bit_depth - 1 & 7);
+}
+
+// this macro is supposed to work on all formats supported by 3D rendering, and
+// that produce "reasonable" output (i.e. no mixed up colors)
+#define IMGFMT_IS_ANY_RND(x) \
+ (IMGFMT_IS_BGR(x) || IMGFMT_IS_RGB(x) || IMGFMT_IS_Y(x))
+
+// pixel size in bit for any IMGFMT_IS_ANY_RND(x)==true
+// we assume that the actual pixel strides are always aligned on bytes
+static int imgfmt_any_rnd_depth(int fmt)
+{
+ if (IMGFMT_IS_BGR(fmt))
+ return IMGFMT_BGR_DEPTH(fmt);
+ if (IMGFMT_IS_RGB(fmt))
+ return IMGFMT_RGB_DEPTH(fmt);
+ if (IMGFMT_IS_Y(fmt))
+ return IMGFMT_Y_DEPTH(fmt);
+ assert(false);
+ return 0;
+}
+
+static D3DFORMAT check_format(d3d_priv *priv, uint32_t movie_fmt,
+ bool as_texture)
+{
+ const char *type = as_texture ? "texture rendering" : "StretchRect";
+ const struct fmt_entry *cur = &fmt_table[0];
+
+ // Don't try to handle weird packed texture formats (although I don't know
+ // if D3D9 would even accept any such format for 3D rendering; and we
+ // certainly don't try any tricks like matching it to RGB formats and
+ // applying a YUV conversion matrix)
+ if (as_texture && !IMGFMT_IS_ANY_RND(movie_fmt))
+ return 0;
+
+ while (cur->mplayer_fmt) {
+ if (cur->mplayer_fmt == movie_fmt) {
+ HRESULT res;
+ if (as_texture) {
+ res = IDirect3D9_CheckDeviceFormat(priv->d3d_handle,
+ D3DADAPTER_DEFAULT,
+ DEVTYPE,
+ priv->desktop_fmt,
+ D3DUSAGE_DYNAMIC | D3DUSAGE_QUERY_FILTER,
+ D3DRTYPE_TEXTURE,
+ cur->fourcc);
+ } else {
+ /* Test conversion from Movie colorspace to
+ * display's target colorspace. */
+ res = IDirect3D9_CheckDeviceFormatConversion(priv->d3d_handle,
+ D3DADAPTER_DEFAULT,
+ DEVTYPE,
+ cur->fourcc,
+ priv->desktop_fmt);
+ }
+ if (FAILED(res)) {
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Rejected image format "
+ "(%s): %s\n", type, vo_format_name(cur->mplayer_fmt));
+ return 0;
+ }
+
+ mp_msg(MSGT_VO, MSGL_DBG2, "<vo_direct3d>Accepted image format "
+ "(%s): %s\n", type, vo_format_name(cur->mplayer_fmt));
+
+ return cur->fourcc;
+ }
+ cur++;
+ }
+
+ return 0;
+}
+
+// Check whether YUV conversion with shaders can be done.
+// Returns the format the individual planes should use (or 0 on failure)
+static D3DFORMAT check_shader_conversion(d3d_priv *priv, uint32_t fmt)
+{
+ if (priv->opt_disable_shaders)
+ return 0;
+ int component_bits;
+ if (!mp_get_chroma_shift(fmt, NULL, NULL, &component_bits))
+ return 0;
+ if (component_bits < 8 || component_bits > 16)
+ return 0;
+ bool is_8bit = component_bits == 8;
+ if (!is_8bit && priv->opt_only_8bit)
+ return 0;
+ int texfmt = IMGFMT_Y8;
+ if (!is_8bit) {
+ if (priv->opt_16bit_textures)
+ texfmt = IMGFMT_Y16;
+ else
+ texfmt = IMGFMT_A8Y8;
+ }
+ return check_format(priv, texfmt, true);
+}
+
+// Return if the image format can be used. If it can, decide which rendering
+// and conversion mode to use.
+// If initialize is true, actually setup all variables to use the picked
+// rendering mode.
+static bool init_rendering_mode(d3d_priv *priv, uint32_t fmt, bool initialize)
+{
+ int n;
+ int blit_d3dfmt = check_format(priv, fmt, false);
+ int texture_d3dfmt = check_format(priv, fmt, true);
+ int shader_d3dfmt = check_shader_conversion(priv, fmt);
+
+ if (priv->opt_disable_textures)
+ texture_d3dfmt = 0;
+ if (priv->opt_disable_shaders)
+ shader_d3dfmt = 0;
+ if (priv->opt_disable_stretchrect)
+ blit_d3dfmt = 0;
+
+ if (!(blit_d3dfmt || shader_d3dfmt || texture_d3dfmt))
+ return false;
+
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Accepted rendering methods for "
+ "format='%s': StretchRect=%#x, Texture=%#x, Texture+Shader=%#x.\n",
+ vo_format_name(fmt), blit_d3dfmt, texture_d3dfmt, shader_d3dfmt);
+
+ if (!initialize)
+ return true;
+
+ // initialization doesn't fail beyond this point
+
+ priv->use_shaders = false;
+ priv->use_textures = false;
+ priv->use_2ch_hack = false;
+ priv->movie_src_fmt = 0;
+ priv->pixel_shader_data = NULL;
+ priv->plane_count = 0;
+ priv->image_format = fmt;
+
+ if (blit_d3dfmt && priv->opt_prefer_stretchrect)
+ texture_d3dfmt = shader_d3dfmt = 0;
+
+ if (texture_d3dfmt) {
+ priv->use_textures = true;
+ } else if (shader_d3dfmt) {
+ priv->use_textures = true;
+ priv->use_shaders = true;
+ } else {
+ assert(!!blit_d3dfmt);
+ }
+
+ if (priv->use_textures) {
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Using 3D rendering.\n");
+
+ struct texplane *planes = &priv->planes[0];
+ planes[0].shift_x = planes[0].shift_y = 0;
+ planes[0].clearval = 0;
+
+ if (!priv->use_shaders) {
+ assert(IMGFMT_IS_ANY_RND(priv->image_format));
+ priv->plane_count = 1;
+ planes[0].bits_per_pixel = imgfmt_any_rnd_depth(priv->image_format);
+ planes[0].d3d_format = texture_d3dfmt;
+ } else {
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Using YUV shaders.\n");
+
+ int sx, sy, component_bits;
+ mp_get_chroma_shift(priv->image_format, &sx, &sy, &component_bits);
+ priv->plane_count = 3;
+ for (n = 0; n < 3; n++) {
+ planes[n].d3d_format = shader_d3dfmt;
+ planes[n].bits_per_pixel = component_bits;
+ if (n > 0) {
+ planes[n].shift_x = sx;
+ planes[n].shift_y = sy;
+ planes[n].clearval = get_chroma_clear_val(component_bits);
+ }
+ }
+ if (shader_d3dfmt != D3DFMT_A8L8) {
+ priv->pixel_shader_data = d3d_shader_yuv;
+ } else {
+ mp_msg(MSGT_VO, MSGL_WARN, "<vo_direct3d>Using YUV 2ch hack.\n");
+
+ priv->pixel_shader_data = d3d_shader_yuv_2ch;
+ priv->use_2ch_hack = true;
+ }
+ }
+
+ for (n = 0; n < priv->plane_count; n++) {
+ planes[n].bytes_per_pixel = (planes[n].bits_per_pixel + 7) / 8;
+ }
+
+ } else {
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Using StretchRect.\n");
+
+ priv->movie_src_fmt = blit_d3dfmt;
+ }
+
+ update_colorspace(priv);
+
+ return true;
+}
+
+/** @brief Query if movie colorspace is supported by the HW.
+ * @return 0 on failure, device capabilities (not probed
+ * currently) on success.
+ */
+static int query_format(d3d_priv *priv, uint32_t movie_fmt)
+{
+ if (!init_rendering_mode(priv, movie_fmt, false))
+ return 0;
+
+ int osd_caps = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW
+ | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN;
+ if (!priv->opt_disable_osd)
+ osd_caps |= VFCAP_OSD;
+ return osd_caps;
+}
+
+/****************************************************************************
+ * *
+ * *
+ * *
+ * libvo Control / Callback functions *
+ * *
+ * *
+ * *
+ ****************************************************************************/
+
+static void get_2ch_depth_multiplier(int depth, float *out_f1, float *out_f2) {
+ // How to get these values:
+ // The suffix i8 and i16 is for values with 8/16 bit fixed point numbers.
+ // The suffix f is for float, ideally in the range 0.0-1.0.
+ // c_i8 is a two component vector, sampled from a two channel texture.
+ // (c_i8.x is the low byte, c_i8.y is the high byte)
+ // r_f is the resulting color scalar value.
+ //
+ // c_i8 = c_f * (2^8-1)
+ // r_i16 = c_i8.x + c_i8.y * 2^8
+ // r_f = r_i16 / (2^16-1)
+ // = c_f.x * (2^8-1) / (2^16-1) + c_f.y * (2^8-1) * 2^8 / (2^16-1)
+ // = c_f.x * ((2^8-1) / (2^16-1)) + c_f.y * (2^8 * ((2^8-1) / (2^16-1)))
+ // out = ((2^8-1) / (2^16-1), 2^8 * ((2^8-1) / (2^16-1)))
+ // The result color is r_f = dot(c_f, out).
+ // Same goes for other bit depth, such as 10 bit. Assuming (2^depth-1) is
+ // the maximum possible value at that depth, you have to scale the value
+ // r_i16 with it, the factor (2^16-1) in the formula above has to be
+ // replaced with (2^depth-1).
+ float factor = (float)((1 << 8) - 1) / (float)((1 << depth) - 1);
+ *out_f1 = factor;
+ *out_f2 = 256.0 * factor;
+}
+
+static void update_colorspace(d3d_priv *priv)
+{
+ float coeff[3][4];
+ struct mp_csp_params csp = { .colorspace = priv->colorspace };
+ mp_csp_copy_equalizer_values(&csp, &priv->video_eq);
+
+ if (priv->use_shaders) {
+ if (!priv->use_2ch_hack) {
+ csp.input_bits = priv->planes[0].bits_per_pixel;
+ csp.texture_bits = (csp.input_bits + 7) & ~7;
+ } else {
+ float f1, f2;
+ get_2ch_depth_multiplier(priv->planes[0].bits_per_pixel, &f1, &f2);
+ priv->d3d_depth_vector[0] = f1;
+ priv->d3d_depth_vector[1] = f2;
+ priv->d3d_depth_vector[2] = priv->d3d_depth_vector[3] = 0;
+ // no change
+ csp.input_bits = 8;
+ csp.texture_bits = 8;
+ }
+
+ mp_get_yuv2rgb_coeffs(&csp, coeff);
+ for (int row = 0; row < 3; row++) {
+ for (int col = 0; col < 4; col++) {
+ priv->d3d_colormatrix.m[row][col] = coeff[row][col];
+ }
+ }
+ }
+}
+
+const char *options_help_text = "-vo direct3d command line help:\n"
+"Example: -vo direct3d:disable-osd:disable-textures\n"
+"Options:\n"
+" prefer-stretchrect\n"
+" Use IDirect3DDevice9::StretchRect over other methods if possible.\n"
+" disable-stretchrect\n"
+" Never render the video using IDirect3DDevice9::StretchRect.\n"
+" disable-textures\n"
+" Never render the video using D3D texture rendering. (Rendering with\n"
+" textures + shader will still be allowed. Add disable-shaders to\n"
+" completely disable video rendering with textures.)\n"
+" disable-shaders\n"
+" Never use shaders when rendering video.\n"
+" only-8bit\n"
+" Never render YUV video with more than 8 bits per component.\n"
+" (Using this flag will force software conversion to 8 bit.)\n"
+" disable-osd\n"
+" Disable OSD rendering.\n"
+" (Using this flag might force the insertion of the 'ass' video filter,\n"
+" which will render the subtitles in software.)\n"
+" disable-texture-align\n"
+" Normally texture sizes are always aligned to 16. With this option\n"
+" enabled, the video texture will always have exactly the same size as\n"
+" the video itself.\n"
+"Debug options. These might be incorrect, might be removed in the future, might\n"
+"crash, might cause slow downs, etc. Contact the developers if you actually need\n"
+"any of these for performance or proper operation.\n"
+" force-power-of-2\n"
+" Always force textures to power of 2, even if the device reports\n"
+" non-power-of-2 texture sizes as supported.\n"
+" texture-memory=N\n"
+" Only affects operation with shaders/texturing enabled, and (E)OSD.\n"
+" Values for N:\n"
+" 0 default, will often use an additional shadow texture + copy\n"
+" 1 use D3DPOOL_MANAGED\n"
+" 2 use D3DPOOL_DEFAULT\n"
+" 3 use D3DPOOL_SYSTEMMEM, but without shadow texture\n"
+" swap-discard\n"
+" Use D3DSWAPEFFECT_DISCARD, which might be faster.\n"
+" Might be slower too, as it must (?) clear every frame.\n"
+" exact-backbuffer\n"
+" Always resize the backbuffer to window size.\n"
+" no16bit-textures\n"
+" Don't use textures with a 16 bit color channel for YUV formats that\n"
+" use more than 8 bits per component. Instead, use D3DFMT_A8L8 textures\n"
+" and compute the values sampled from the 2 channels back into one.\n"
+" Might be slower, since the shader becomes slightly more complicated.\n"
+" Might work better, if your drivers either don't support D3DFMT_L16,\n"
+" or if either the texture unit or the shaders don't operate in at least\n"
+" 16 bit precision.\n"
+"";
+
+/** @brief libvo Callback: Preinitialize the video card.
+ * Preinit the hardware just enough to be queried about
+ * supported formats.
+ *
+ * @return 0 on success, -1 on failure
+ */
+
+static int preinit_internal(struct vo *vo, const char *arg, bool allow_shaders)
+{
+ d3d_priv *priv = talloc_zero(vo, d3d_priv);
+ vo->priv = priv;
+
+ *priv = (d3d_priv) {
+ .vo = vo,
+
+ .opt_16bit_textures = true,
+
+ .colorspace = MP_CSP_DETAILS_DEFAULTS,
+ .video_eq = { MP_CSP_EQ_CAPS_COLORMATRIX },
+ };
+
+ for (int n = 0; n < MAX_OSD_PARTS; n++) {
+ struct osdpart *osd = talloc_ptrtype(priv, osd);
+ *osd = (struct osdpart) {
+ .packer = talloc_zero(osd, struct bitmap_packer),
+ };
+ priv->osd[n] = osd;
+ }
+
+ if (!allow_shaders) {
+ priv->opt_disable_shaders = priv->opt_disable_textures = true;
+ }
+
+ const opt_t subopts[] = {
+ {"prefer-stretchrect", OPT_ARG_BOOL, &priv->opt_prefer_stretchrect},
+ {"disable-textures", OPT_ARG_BOOL, &priv->opt_disable_textures},
+ {"disable-stretchrect", OPT_ARG_BOOL, &priv->opt_disable_stretchrect},
+ {"disable-shaders", OPT_ARG_BOOL, &priv->opt_disable_shaders},
+ {"only-8bit", OPT_ARG_BOOL, &priv->opt_only_8bit},
+ {"disable-osd", OPT_ARG_BOOL, &priv->opt_disable_osd},
+ {"force-power-of-2", OPT_ARG_BOOL, &priv->opt_force_power_of_2},
+ {"disable-texture-align", OPT_ARG_BOOL, &priv->opt_disable_texture_align},
+ {"texture-memory", OPT_ARG_INT, &priv->opt_texture_memory},
+ {"swap-discard", OPT_ARG_BOOL, &priv->opt_swap_discard},
+ {"exact-backbuffer", OPT_ARG_BOOL, &priv->opt_exact_backbuffer},
+ {"16bit-textures", OPT_ARG_BOOL, &priv->opt_16bit_textures},
+ {NULL}
+ };
+ if (subopt_parse(arg, subopts) != 0) {
+ mp_msg(MSGT_VO, MSGL_FATAL, options_help_text);
+ return -1;
+ }
+
+ priv->d3d9_dll = LoadLibraryA("d3d9.dll");
+ if (!priv->d3d9_dll) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "<vo_direct3d>Unable to dynamically load d3d9.dll\n");
+ goto err_out;
+ }
+
+ priv->pDirect3DCreate9 = (void *)GetProcAddress(priv->d3d9_dll,
+ "Direct3DCreate9");
+ if (!priv->pDirect3DCreate9) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "<vo_direct3d>Unable to find entry point of Direct3DCreate9\n");
+ goto err_out;
+ }
+
+ if (!init_d3d(priv))
+ goto err_out;
+
+ /* w32_common framework call. Configures window on the screen, gets
+ * fullscreen dimensions and does other useful stuff.
+ */
+ if (!vo_w32_init(vo)) {
+ mp_msg(MSGT_VO, MSGL_V,
+ "<vo_direct3d>Configuring onscreen window failed.\n");
+ goto err_out;
+ }
+
+ return 0;
+
+err_out:
+ uninit(vo);
+ return -1;
+}
+
+static int preinit_standard(struct vo *vo, const char *arg)
+{
+ return preinit_internal(vo, arg, false);
+}
+
+static int preinit_shaders(struct vo *vo, const char *arg)
+{
+ return preinit_internal(vo, arg, true);
+}
+
+/** @brief libvo Callback: Handle control requests.
+ * @return VO_TRUE on success, VO_NOTIMPL when not implemented
+ */
+static int control(struct vo *vo, uint32_t request, void *data)
+{
+ d3d_priv *priv = vo->priv;
+
+ switch (request) {
+ case VOCTRL_QUERY_FORMAT:
+ return query_format(priv, *(uint32_t*) data);
+ case VOCTRL_DRAW_IMAGE:
+ return d3d_upload_and_render_frame(priv, data);
+ case VOCTRL_FULLSCREEN:
+ vo_w32_fullscreen(vo);
+ resize_d3d(priv);
+ return VO_TRUE;
+ case VOCTRL_RESET:
+ return VO_NOTIMPL;
+ case VOCTRL_REDRAW_FRAME:
+ priv->is_clear_needed = 1;
+ d3d_draw_frame(priv);
+ return VO_TRUE;
+ case VOCTRL_SET_YUV_COLORSPACE:
+ priv->colorspace = *(struct mp_csp_details *)data;
+ update_colorspace(priv);
+ vo->want_redraw = true;
+ return VO_TRUE;
+ case VOCTRL_GET_YUV_COLORSPACE:
+ if (!priv->use_shaders)
+ break; // no idea what the heck D3D YUV uses
+ *(struct mp_csp_details *)data = priv->colorspace;
+ return VO_TRUE;
+ case VOCTRL_SET_EQUALIZER: {
+ if (!priv->use_shaders)
+ break;
+ struct voctrl_set_equalizer_args *args = data;
+ if (mp_csp_equalizer_set(&priv->video_eq, args->name, args->value) < 0)
+ return VO_NOTIMPL;
+ update_colorspace(priv);
+ vo->want_redraw = true;
+ return VO_TRUE;
+ }
+ case VOCTRL_GET_EQUALIZER: {
+ if (!priv->use_shaders)
+ break;
+ struct voctrl_get_equalizer_args *args = data;
+ return mp_csp_equalizer_get(&priv->video_eq, args->name, args->valueptr)
+ >= 0 ? VO_TRUE : VO_NOTIMPL;
+ }
+ case VOCTRL_ONTOP:
+ vo_w32_ontop(vo);
+ return VO_TRUE;
+ case VOCTRL_BORDER:
+ vo_w32_border(vo);
+ return VO_TRUE;
+ case VOCTRL_UPDATE_SCREENINFO:
+ w32_update_xinerama_info(vo);
+ return VO_TRUE;
+ case VOCTRL_SET_PANSCAN:
+ calc_fs_rect(priv);
+ return VO_TRUE;
+ case VOCTRL_GET_PANSCAN:
+ return VO_TRUE;
+ case VOCTRL_SCREENSHOT: {
+ struct voctrl_screenshot_args *args = data;
+ if (args->full_window)
+ args->out_image = get_window_screenshot(priv);
+ else
+ args->out_image = get_screenshot(priv);
+ return !!args->out_image;
+ }
+ }
+ return VO_NOTIMPL;
+}
+
+/** @brief libvo Callback: Configre the Direct3D adapter.
+ * @param width Movie source width
+ * @param height Movie source height
+ * @param d_width Screen (destination) width
+ * @param d_height Screen (destination) height
+ * @param options Options bitmap
+ * @param format Movie colorspace format (using MPlayer's
+ * defines, e.g. IMGFMT_YUY2)
+ * @return 0 on success, VO_ERROR on failure
+ */
+static int config(struct vo *vo, uint32_t width, uint32_t height,
+ uint32_t d_width, uint32_t d_height, uint32_t options,
+ uint32_t format)
+{
+ d3d_priv *priv = vo->priv;
+
+ /* w32_common framework call. Creates window on the screen with
+ * the given coordinates.
+ */
+ if (!vo_w32_config(vo, d_width, d_height, options)) {
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Creating window failed.\n");
+ return VO_ERROR;
+ }
+
+ if ((priv->image_format != format)
+ || (priv->src_width != width)
+ || (priv->src_height != height))
+ {
+ d3d_destroy_video_objects(priv);
+
+ priv->src_width = width;
+ priv->src_height = height;
+ init_rendering_mode(priv, format, true);
+ }
+
+ if (!resize_d3d(priv))
+ return VO_ERROR;
+
+ return 0; /* Success */
+}
+
+/** @brief libvo Callback: Flip next already drawn frame on the
+ * screen.
+ */
+static void flip_page(struct vo *vo)
+{
+ d3d_priv *priv = vo->priv;
+
+ if (priv->d3d_device && priv->d3d_in_scene) {
+ if (FAILED(IDirect3DDevice9_EndScene(priv->d3d_device))) {
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>EndScene failed.\n");
+ }
+ }
+ priv->d3d_in_scene = false;
+
+ RECT rect = {0, 0, vo->dwidth, vo->dheight};
+ if (!priv->d3d_device ||
+ FAILED(IDirect3DDevice9_Present(priv->d3d_device, &rect, 0, 0, 0))) {
+ mp_msg(MSGT_VO, MSGL_V,
+ "<vo_direct3d>Trying to reinitialize uncooperative video adapter.\n");
+ if (!reconfigure_d3d(priv)) {
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Reinitialization failed.\n");
+ return;
+ } else {
+ mp_msg(MSGT_VO, MSGL_V,
+ "<vo_direct3d>Video adapter reinitialized.\n");
+ }
+ }
+}
+
+/** @brief libvo Callback: Uninitializes all pointers and closes
+ * all D3D related stuff,
+ */
+static void uninit(struct vo *vo)
+{
+ d3d_priv *priv = vo->priv;
+
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>uninit called.\n");
+
+ uninit_d3d(priv);
+ vo_w32_uninit(vo);
+ if (priv->d3d9_dll)
+ FreeLibrary(priv->d3d9_dll);
+ priv->d3d9_dll = NULL;
+}
+
+/** @brief libvo Callback: Handles video window events.
+ */
+static void check_events(struct vo *vo)
+{
+ d3d_priv *priv = vo->priv;
+
+ int flags = vo_w32_check_events(vo);
+ if (flags & VO_EVENT_RESIZE)
+ resize_d3d(priv);
+
+ if (flags & VO_EVENT_EXPOSE)
+ vo->want_redraw = true;
+}
+
+static int draw_slice_textures(d3d_priv *priv, uint8_t *src[], int stride[],
+ int w, int h, int x, int y)
+{
+ if (!d3d_lock_video_textures(priv))
+ return VO_FALSE;
+
+ for (int n = 0; n < priv->plane_count; n++) {
+ struct texplane *plane = &priv->planes[n];
+
+ int dst_stride = plane->locked_rect.Pitch;
+ uint8_t *pdst = (uint8_t*)plane->locked_rect.pBits
+ + (y >> plane->shift_y) * dst_stride
+ + (x >> plane->shift_x) * plane->bytes_per_pixel;
+
+ memcpy_pic(pdst, src[n], (w >> plane->shift_x) * plane->bytes_per_pixel,
+ h >> plane->shift_y, dst_stride, stride[n]);
+ }
+
+ return 0;
+}
+
+/** @brief libvo Callback: Draw slice
+ * @return 0 on success
+ */
+static int draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h,
+ int x, int y)
+{
+ d3d_priv *priv = vo->priv;
+
+ if (!priv->d3d_device)
+ return 0;
+
+ char *my_src; /**< Pointer to the source image */
+ char *dst; /**< Pointer to the destination image */
+ int uv_stride; /**< Stride of the U/V planes */
+
+ if (priv->use_textures)
+ return draw_slice_textures(priv, src, stride, w, h, x, y);
+
+ /* Lock the offscreen surface if it's not already locked. */
+ if (!priv->locked_rect.pBits) {
+ if (FAILED(IDirect3DSurface9_LockRect(priv->d3d_surface,
+ &priv->locked_rect, NULL, 0))) {
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Surface lock failure.\n");
+ return VO_FALSE;
+ }
+ }
+
+ uv_stride = priv->locked_rect.Pitch / 2;
+
+ /* Copy Y */
+ dst = priv->locked_rect.pBits;
+ dst = dst + priv->locked_rect.Pitch * y + x;
+ my_src = src[0];
+ memcpy_pic(dst, my_src, w, h, priv->locked_rect.Pitch, stride[0]);
+
+ w /= 2;
+ h /= 2;
+ x /= 2;
+ y /= 2;
+
+ /* Copy U */
+ dst = priv->locked_rect.pBits;
+ dst = dst + priv->locked_rect.Pitch * priv->src_height
+ + uv_stride * y + x;
+ if (priv->movie_src_fmt == MAKEFOURCC('Y','V','1','2'))
+ my_src = src[2];
+ else
+ my_src = src[1];
+
+ memcpy_pic(dst, my_src, w, h, uv_stride, stride[1]);
+
+ /* Copy V */
+ dst = priv->locked_rect.pBits;
+ dst = dst + priv->locked_rect.Pitch * priv->src_height
+ + uv_stride * (priv->src_height / 2) + uv_stride * y + x;
+ if (priv->movie_src_fmt == MAKEFOURCC('Y','V','1','2'))
+ my_src=src[1];
+ else
+ my_src=src[2];
+
+ memcpy_pic(dst, my_src, w, h, uv_stride, stride[2]);
+
+ return 0; /* Success */
+}
+
+static bool get_screenshot_from_surface(d3d_priv *priv, mp_image_t *image)
+{
+ if (!priv->locked_rect.pBits) {
+ if (FAILED(IDirect3DSurface9_LockRect(priv->d3d_surface,
+ &priv->locked_rect, NULL, 0))) {
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Surface lock failed.\n");
+ return false;
+ }
+ }
+
+ if (image->flags & MP_IMGFLAG_PLANAR) {
+ char *src;
+ int w = priv->src_width;
+ int h = priv->src_height;
+ int swapped = priv->movie_src_fmt == MAKEFOURCC('Y','V','1','2');
+ int plane1 = swapped ? 2 : 1;
+ int plane2 = swapped ? 1 : 2;
+
+ int uv_stride = priv->locked_rect.Pitch / 2;
+
+ /* Copy Y */
+ src = priv->locked_rect.pBits;
+ memcpy_pic(image->planes[0], src, w, h, priv->locked_rect.Pitch,
+ image->stride[0]);
+
+ w /= 2;
+ h /= 2;
+
+ /* Copy U */
+ src = priv->locked_rect.pBits;
+ src = src + priv->locked_rect.Pitch * priv->src_height;
+
+ memcpy_pic(image->planes[plane1], src, w, h, uv_stride,
+ image->stride[1]);
+
+ /* Copy V */
+ src = priv->locked_rect.pBits;
+ src = src + priv->locked_rect.Pitch * priv->src_height
+ + uv_stride * (priv->src_height / 2);
+
+ memcpy_pic(image->planes[plane2], src, w, h, uv_stride,
+ image->stride[2]);
+ } else {
+ // packed YUV or RGB
+ memcpy_pic(image->planes[0], priv->locked_rect.pBits, image->stride[0],
+ image->height, priv->locked_rect.Pitch, image->stride[0]);
+ }
+
+ d3d_unlock_video_objects(priv);
+ return true;
+}
+
+static bool get_screenshot_from_texture(d3d_priv *priv, mp_image_t *image)
+{
+ if (!d3d_lock_video_textures(priv)) {
+ d3d_unlock_video_objects(priv);
+ return false;
+ }
+
+ assert(image->num_planes == priv->plane_count);
+
+ for (int n = 0; n < priv->plane_count; n++) {
+ struct texplane *plane = &priv->planes[n];
+
+ int width = priv->src_width >> plane->shift_x;
+ int height = priv->src_height >> plane->shift_y;
+
+ memcpy_pic(image->planes[n], plane->locked_rect.pBits,
+ width * plane->bytes_per_pixel, height,
+ image->stride[n], plane->locked_rect.Pitch);
+ }
+
+ return true;
+}
+
+static mp_image_t *get_screenshot(d3d_priv *priv)
+{
+ if (!priv->d3d_device)
+ return NULL;
+
+ mp_image_t *image = alloc_mpi(priv->src_width, priv->src_height,
+ priv->image_format);
+
+ bool res;
+
+ if (priv->use_textures)
+ res = get_screenshot_from_texture(priv, image);
+ else
+ res = get_screenshot_from_surface(priv, image);
+
+ if (!res) {
+ free_mp_image(image);
+ return NULL;
+ }
+
+ image->display_w = priv->vo->aspdat.prew;
+ image->display_h = priv->vo->aspdat.preh;
+
+ mp_image_set_colorspace_details(image, &priv->colorspace);
+
+ return image;
+}
+
+static mp_image_t *get_window_screenshot(d3d_priv *priv)
+{
+ D3DDISPLAYMODE mode;
+ mp_image_t *image = NULL;
+ RECT window_rc;
+ RECT screen_rc;
+ RECT visible;
+ POINT pt;
+ D3DLOCKED_RECT locked_rect;
+ int width, height;
+
+ if (FAILED(IDirect3DDevice9_GetDisplayMode(priv->d3d_device, 0, &mode))) {
+ mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>GetDisplayMode failed.\n");
+ goto error_exit;
+ }
+
+ IDirect3DSurface9 *surface = NULL;
+ if (FAILED(IDirect3DDevice9_CreateOffscreenPlainSurface(priv->d3d_device,
+ mode.Width, mode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &surface,
+ NULL)))
+ {
+ mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>Couldn't create surface.\n");
+ goto error_exit;
+ }
+
+ if (FAILED(IDirect3DDevice9_GetFrontBufferData(priv->d3d_device, 0,
+ surface)))
+ {
+ mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>Couldn't copy frontbuffer.\n");
+ goto error_exit;
+ }
+
+ GetClientRect(priv->vo->w32->window, &window_rc);
+ pt = (POINT) { 0, 0 };
+ ClientToScreen(priv->vo->w32->window, &pt);
+ window_rc.left = pt.x;
+ window_rc.top = pt.y;
+ window_rc.right += window_rc.left;
+ window_rc.bottom += window_rc.top;
+
+ screen_rc = (RECT) { 0, 0, mode.Width, mode.Height };
+
+ if (!IntersectRect(&visible, &screen_rc, &window_rc))
+ goto error_exit;
+ width = visible.right - visible.left;
+ height = visible.bottom - visible.top;
+ if (width < 1 || height < 1)
+ goto error_exit;
+
+ image = alloc_mpi(width, height, IMGFMT_BGR32);
+
+ IDirect3DSurface9_LockRect(surface, &locked_rect, NULL, 0);
+
+ memcpy_pic(image->planes[0], (char*)locked_rect.pBits + visible.top *
+ locked_rect.Pitch + visible.left * 4, width * 4, height,
+ image->stride[0], locked_rect.Pitch);
+
+ IDirect3DSurface9_UnlockRect(surface);
+ IDirect3DSurface9_Release(surface);
+
+ return image;
+
+error_exit:
+ if (image)
+ free_mp_image(image);
+ if (surface)
+ IDirect3DSurface9_Release(surface);
+ return NULL;
+}
+
+static bool upload_osd(d3d_priv *priv, struct osdpart *osd,
+ struct sub_bitmaps *imgs)
+{
+ D3DFORMAT fmt = osd_fmt_table[imgs->format];
+
+ osd->packer->w_max = priv->max_texture_width;
+ osd->packer->h_max = priv->max_texture_height;
+
+ osd->packer->padding = imgs->scaled; // assume 2x2 filter on scaling
+ int r = packer_pack_from_subbitmaps(osd->packer, imgs);
+ if (r < 0) {
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>OSD bitmaps do not fit on "
+ "a surface with the maximum supported size %dx%d.\n",
+ osd->packer->w_max, osd->packer->h_max);
+ return false;
+ }
+
+ if (osd->packer->w > osd->texture.tex_w
+ || osd->packer->h > osd->texture.tex_h
+ || osd->format != imgs->format)
+ {
+ osd->format = imgs->format;
+
+ int new_w = osd->packer->w;
+ int new_h = osd->packer->h;
+ d3d_fix_texture_size(priv, &new_w, &new_h);
+
+ mp_msg(MSGT_VO, MSGL_DBG2, "<vo_direct3d>reallocate OSD surface.\n");
+
+ d3dtex_release(priv, &osd->texture);
+ d3dtex_allocate(priv, &osd->texture, fmt, new_w, new_h);
+
+ if (!osd->texture.system)
+ return false; // failed to allocate
+ }
+
+ struct pos bb[2];
+ packer_get_bb(osd->packer, bb);
+ RECT dirty_rc = { bb[0].x, bb[0].y, bb[1].x, bb[1].y };
+
+ D3DLOCKED_RECT locked_rect;
+
+ if (FAILED(IDirect3DTexture9_LockRect(osd->texture.system, 0, &locked_rect,
+ &dirty_rc, 0)))
+ {
+ mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>OSD texture lock failed.\n");
+ return false;
+ }
+
+ int ps = fmt == D3DFMT_A8 ? 1 : 4;
+ packer_copy_subbitmaps(osd->packer, imgs, locked_rect.pBits, ps,
+ locked_rect.Pitch);
+
+ if (FAILED(IDirect3DTexture9_UnlockRect(osd->texture.system, 0))) {
+ mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>OSD texture unlock failed.\n");
+ return false;
+ }
+
+ return d3dtex_update(priv, &osd->texture);
+}
+
+static struct osdpart *generate_osd(d3d_priv *priv, struct sub_bitmaps *imgs)
+{
+ if (imgs->num_parts == 0 || !osd_fmt_table[imgs->format])
+ return NULL;
+
+ struct osdpart *osd = priv->osd[imgs->render_index];
+
+ if (imgs->bitmap_pos_id != osd->bitmap_pos_id) {
+ if (imgs->bitmap_id != osd->bitmap_id) {
+ if (!upload_osd(priv, osd, imgs))
+ osd->packer->count = 0;
+ }
+
+ osd->bitmap_id = imgs->bitmap_id;
+ osd->bitmap_pos_id = imgs->bitmap_pos_id;
+ osd->num_vertices = 0;
+ }
+
+ return osd->packer->count ? osd : NULL;
+}
+
+static D3DCOLOR ass_to_d3d_color(uint32_t color)
+{
+ uint32_t r = (color >> 24) & 0xff;
+ uint32_t g = (color >> 16) & 0xff;
+ uint32_t b = (color >> 8) & 0xff;
+ uint32_t a = 0xff - (color & 0xff);
+ return D3DCOLOR_ARGB(a, r, g, b);
+}
+
+static void draw_osd_cb(void *ctx, struct sub_bitmaps *imgs)
+{
+ d3d_priv *priv = ctx;
+
+ struct osdpart *osd = generate_osd(priv, imgs);
+ if (!osd)
+ return;
+
+ if (osd->packer->count && !osd->num_vertices) {
+ // We need 2 primitives per quad which makes 6 vertices (we could reduce
+ // the number of vertices by using an indexed vertex array, but it's
+ // probably not worth doing)
+ osd->num_vertices = osd->packer->count * 6;
+ osd->vertices = talloc_realloc(osd, osd->vertices, vertex_osd,
+ osd->num_vertices);
+
+ float tex_w = osd->texture.tex_w;
+ float tex_h = osd->texture.tex_h;
+
+ for (int n = 0; n < osd->packer->count; n++) {
+ struct sub_bitmap *b = &imgs->parts[n];
+ struct pos p = osd->packer->result[n];
+
+ D3DCOLOR color = imgs->format == SUBBITMAP_LIBASS
+ ? ass_to_d3d_color(b->libass.color)
+ : D3DCOLOR_ARGB(255, 255, 255, 255);
+
+ float x0 = b->x;
+ float y0 = b->y;
+ float x1 = b->x + b->dw;
+ float y1 = b->y + b->dh;
+ float tx0 = p.x / tex_w;
+ float ty0 = p.y / tex_h;
+ float tx1 = (p.x + b->w) / tex_w;
+ float ty1 = (p.y + b->h) / tex_h;
+
+ vertex_osd *v = &osd->vertices[n * 6];
+ v[0] = (vertex_osd) { x0, y0, 0, color, tx0, ty0 };
+ v[1] = (vertex_osd) { x1, y0, 0, color, tx1, ty0 };
+ v[2] = (vertex_osd) { x0, y1, 0, color, tx0, ty1 };
+ v[3] = (vertex_osd) { x1, y1, 0, color, tx1, ty1 };
+ v[4] = v[2];
+ v[5] = v[1];
+ }
+ }
+
+ d3d_begin_scene(priv);
+
+ IDirect3DDevice9_SetRenderState(priv->d3d_device,
+ D3DRS_ALPHABLENDENABLE, TRUE);
+
+ IDirect3DDevice9_SetTexture(priv->d3d_device, 0,
+ d3dtex_get_render_texture(priv, &osd->texture));
+
+ if (imgs->format == SUBBITMAP_LIBASS) {
+ // do not use the color value from the A8 texture, because that is black
+ IDirect3DDevice9_SetRenderState(priv->d3d_device,D3DRS_TEXTUREFACTOR,
+ 0xFFFFFFFF);
+ IDirect3DDevice9_SetTextureStageState(priv->d3d_device,0,
+ D3DTSS_COLORARG1, D3DTA_TFACTOR);
+
+ IDirect3DDevice9_SetTextureStageState(priv->d3d_device, 0,
+ D3DTSS_ALPHAOP, D3DTOP_MODULATE);
+ } else {
+ IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_SRCBLEND,
+ D3DBLEND_ONE);
+ }
+
+ IDirect3DDevice9_SetFVF(priv->d3d_device, D3DFVF_OSD_VERTEX);
+ IDirect3DDevice9_DrawPrimitiveUP(priv->d3d_device, D3DPT_TRIANGLELIST,
+ osd->num_vertices / 3,
+ osd->vertices, sizeof(vertex_osd));
+
+ IDirect3DDevice9_SetTextureStageState(priv->d3d_device,0,
+ D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ IDirect3DDevice9_SetTextureStageState(priv->d3d_device, 0,
+ D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ IDirect3DDevice9_SetRenderState(priv->d3d_device,
+ D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
+
+ IDirect3DDevice9_SetTexture(priv->d3d_device, 0, NULL);
+
+ IDirect3DDevice9_SetRenderState(priv->d3d_device,
+ D3DRS_ALPHABLENDENABLE, FALSE);
+}
+
+
+static void draw_osd(struct vo *vo, struct osd_state *osd)
+{
+ d3d_priv *priv = vo->priv;
+ if (!priv->d3d_device)
+ return;
+
+ osd_draw(osd, priv->osd_res, osd->vo_pts, 0, osd_fmt_supported,
+ draw_osd_cb, priv);
+}
+
+#define AUTHOR "Georgi Petrov (gogothebee) <gogothebee@gmail.com> and others"
+
+const struct vo_driver video_out_direct3d = {
+ .is_new = true,
+ .info = &(const vo_info_t) {
+ "Direct3D 9 Renderer",
+ "direct3d",
+ AUTHOR,
+ ""
+ },
+ .preinit = preinit_standard,
+ .config = config,
+ .control = control,
+ .draw_slice = draw_slice,
+ .draw_osd = draw_osd,
+ .flip_page = flip_page,
+ .check_events = check_events,
+ .uninit = uninit,
+};
+
+const struct vo_driver video_out_direct3d_shaders = {
+ .is_new = true,
+ .info = &(const vo_info_t) {
+ "Direct3D 9 Renderer (using shaders for YUV conversion)",
+ "direct3d_shaders",
+ AUTHOR,
+ ""
+ },
+ .preinit = preinit_shaders,
+ .config = config,
+ .control = control,
+ .draw_slice = draw_slice,
+ .draw_osd = draw_osd,
+ .flip_page = flip_page,
+ .check_events = check_events,
+ .uninit = uninit,
+};
diff --git a/video/out/vo_image.c b/video/out/vo_image.c
new file mode 100644
index 0000000000..7f80a16f35
--- /dev/null
+++ b/video/out/vo_image.c
@@ -0,0 +1,198 @@
+/*
+ * 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 <math.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+
+#include <libswscale/swscale.h>
+
+#include "config.h"
+#include "bstr.h"
+#include "osdep/io.h"
+#include "path.h"
+#include "talloc.h"
+#include "mp_msg.h"
+#include "libvo/video_out.h"
+#include "libvo/csputils.h"
+#include "libmpcodecs/vfcap.h"
+#include "libmpcodecs/mp_image.h"
+#include "fmt-conversion.h"
+#include "image_writer.h"
+#include "m_config.h"
+#include "m_option.h"
+
+struct priv {
+ struct image_writer_opts *opts;
+ char *outdir;
+
+ int frame;
+
+ uint32_t d_width;
+ uint32_t d_height;
+
+ struct mp_csp_details colorspace;
+};
+
+static bool checked_mkdir(const char *buf)
+{
+ mp_msg(MSGT_VO, MSGL_INFO, "[vo_image] Creating output directory '%s'...\n",
+ buf);
+ if (mkdir(buf, 0755) < 0) {
+ char *errstr = strerror(errno);
+ if (errno == EEXIST) {
+ struct stat stat_p;
+ if (mp_stat(buf, &stat_p ) == 0 && S_ISDIR(stat_p.st_mode))
+ return true;
+ }
+ mp_msg(MSGT_VO, MSGL_ERR, "[vo_image] Error creating output directory"
+ ": %s\n", errstr);
+ return false;
+ }
+ return true;
+}
+
+static int config(struct vo *vo, uint32_t width, uint32_t height,
+ uint32_t d_width, uint32_t d_height, uint32_t flags,
+ uint32_t format)
+{
+ struct priv *p = vo->priv;
+
+ p->d_width = d_width;
+ p->d_height = d_height;
+
+ if (p->outdir && vo->config_count < 1)
+ if (!checked_mkdir(p->outdir))
+ return -1;
+
+ return 0;
+}
+
+static void check_events(struct vo *vo)
+{
+}
+
+static void draw_osd(struct vo *vo, struct osd_state *osd)
+{
+}
+
+static void flip_page(struct vo *vo)
+{
+}
+
+static uint32_t draw_image(struct vo *vo, mp_image_t *mpi)
+{
+ struct priv *p = vo->priv;
+
+ mp_image_t img = *mpi;
+ img.width = p->d_width;
+ img.height = p->d_height;
+ mp_image_set_colorspace_details(&img, &p->colorspace);
+
+ void *t = talloc_new(NULL);
+ char *filename = talloc_asprintf(t, "%08d.%s", p->frame,
+ image_writer_file_ext(p->opts));
+
+ if (p->outdir && strlen(p->outdir))
+ filename = mp_path_join(t, bstr0(p->outdir), bstr0(filename));
+
+ mp_msg(MSGT_VO, MSGL_STATUS, "\nSaving %s\n", filename);
+ write_image(&img, p->opts, filename);
+
+ talloc_free(t);
+
+ (p->frame)++;
+
+ return VO_TRUE;
+}
+
+static int query_format(struct vo *vo, uint32_t fmt)
+{
+ enum PixelFormat av_format = imgfmt2pixfmt(fmt);
+
+ // NOTE: accept everything that can be converted by swscale. screenshot.c
+ // always wants RGB (at least for now), but it probably doesn't matter
+ // whether we or screenshot.c do the conversion.
+ if (av_format != PIX_FMT_NONE && sws_isSupportedInput(av_format))
+ return VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW |
+ VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN;
+ return 0;
+}
+
+static void uninit(struct vo *vo)
+{
+}
+
+static int preinit(struct vo *vo, const char *arg)
+{
+ return 0;
+}
+
+static int control(struct vo *vo, uint32_t request, void *data)
+{
+ struct priv *p = vo->priv;
+
+ switch (request) {
+ case VOCTRL_QUERY_FORMAT:
+ return query_format(vo, *(uint32_t *)data);
+ case VOCTRL_DRAW_IMAGE:
+ return draw_image(vo, data);
+ case VOCTRL_SET_YUV_COLORSPACE:
+ p->colorspace = *(struct mp_csp_details *)data;
+ return true;
+ case VOCTRL_GET_YUV_COLORSPACE:
+ *(struct mp_csp_details *)data = p->colorspace;
+ return true;
+ // prevent random frame stepping by frontend
+ case VOCTRL_REDRAW_FRAME:
+ return true;
+ }
+ return VO_NOTIMPL;
+}
+
+#undef OPT_BASE_STRUCT
+#define OPT_BASE_STRUCT struct priv
+
+const struct vo_driver video_out_image =
+{
+ .is_new = true,
+ .info = &(const vo_info_t) {
+ "Write video frames to image files",
+ "image",
+ "wm4",
+ ""
+ },
+ .priv_size = sizeof(struct priv),
+ .priv_defaults = &(const struct priv) {
+ .colorspace = MP_CSP_DETAILS_DEFAULTS,
+ },
+ .options = (const struct m_option[]) {
+ OPT_SUBSTRUCT(opts, image_writer_conf, M_OPT_MERGE),
+ OPT_STRING("outdir", outdir, 0),
+ {0},
+ },
+ .preinit = preinit,
+ .config = config,
+ .control = control,
+ .draw_osd = draw_osd,
+ .flip_page = flip_page,
+ .check_events = check_events,
+ .uninit = uninit,
+};
diff --git a/video/out/vo_lavc.c b/video/out/vo_lavc.c
new file mode 100644
index 0000000000..b86cd76509
--- /dev/null
+++ b/video/out/vo_lavc.c
@@ -0,0 +1,553 @@
+/*
+ * video encoding using libavformat
+ * Copyright (C) 2010 Nicolas George <george@nsup.org>
+ * Copyright (C) 2011 Rudolf Polzer <divVerent@xonotic.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 "mpcommon.h"
+#include "options.h"
+#include "fmt-conversion.h"
+#include "libmpcodecs/mp_image.h"
+#include "libmpcodecs/vfcap.h"
+#include "subopt-helper.h"
+#include "talloc.h"
+#include "video_out.h"
+
+#include "encode_lavc.h"
+
+#include "sub/sub.h"
+#include "sub/dec_sub.h"
+
+struct priv {
+ uint8_t *buffer;
+ size_t buffer_size;
+ AVStream *stream;
+ int have_first_packet;
+
+ int harddup;
+
+ double lastpts;
+ int64_t lastipts;
+ int64_t lastframeipts;
+ double expected_next_pts;
+ mp_image_t *lastimg;
+ int lastimg_wants_osd;
+ int lastdisplaycount;
+
+ AVRational worst_time_base;
+ int worst_time_base_is_stream;
+
+ struct mp_csp_details colorspace;
+};
+
+static int preinit(struct vo *vo, const char *arg)
+{
+ struct priv *vc;
+ if (!encode_lavc_available(vo->encode_lavc_ctx)) {
+ mp_msg(MSGT_ENCODE, MSGL_ERR,
+ "vo-lavc: the option -o (output file) must be specified\n");
+ return -1;
+ }
+ vo->priv = talloc_zero(vo, struct priv);
+ vc = vo->priv;
+ vc->harddup = vo->encode_lavc_ctx->options->harddup;
+ vc->colorspace = (struct mp_csp_details) MP_CSP_DETAILS_DEFAULTS;
+ return 0;
+}
+
+static void draw_image(struct vo *vo, mp_image_t *mpi, double pts);
+static void uninit(struct vo *vo)
+{
+ struct priv *vc = vo->priv;
+ if (!vc)
+ return;
+
+ if (vc->lastipts >= 0 && vc->stream)
+ draw_image(vo, NULL, MP_NOPTS_VALUE);
+
+ if (vc->lastimg) {
+ // palette hack
+ if (vc->lastimg->imgfmt == IMGFMT_RGB8
+ || vc->lastimg->imgfmt == IMGFMT_BGR8)
+ vc->lastimg->planes[1] = NULL;
+ free_mp_image(vc->lastimg);
+ vc->lastimg = NULL;
+ }
+
+ vo->priv = NULL;
+}
+
+static int config(struct vo *vo, uint32_t width, uint32_t height,
+ uint32_t d_width, uint32_t d_height, uint32_t flags,
+ uint32_t format)
+{
+ struct priv *vc = vo->priv;
+ enum PixelFormat pix_fmt = imgfmt2pixfmt(format);
+ AVRational display_aspect_ratio, image_aspect_ratio;
+ AVRational aspect;
+
+ if (!vc)
+ return -1;
+
+ display_aspect_ratio.num = d_width;
+ display_aspect_ratio.den = d_height;
+ image_aspect_ratio.num = width;
+ image_aspect_ratio.den = height;
+ aspect = av_div_q(display_aspect_ratio, image_aspect_ratio);
+
+ if (vc->stream) {
+ /* NOTE:
+ * in debug builds we get a "comparison between signed and unsigned"
+ * warning here. We choose to ignore that; just because ffmpeg currently
+ * uses a plain 'int' for these struct fields, it doesn't mean it always
+ * will */
+ if (width == vc->stream->codec->width &&
+ height == vc->stream->codec->height) {
+ if (aspect.num != vc->stream->codec->sample_aspect_ratio.num ||
+ aspect.den != vc->stream->codec->sample_aspect_ratio.den) {
+ /* aspect-only changes are not critical */
+ mp_msg(MSGT_ENCODE, MSGL_WARN, "vo-lavc: unsupported pixel aspect "
+ "ratio change from %d:%d to %d:%d\n",
+ vc->stream->codec->sample_aspect_ratio.num,
+ vc->stream->codec->sample_aspect_ratio.den,
+ aspect.num, aspect.den);
+ }
+ return 0;
+ }
+
+ /* FIXME Is it possible with raw video? */
+ mp_msg(MSGT_ENCODE, MSGL_ERR,
+ "vo-lavc: resolution changes not supported.\n");
+ goto error;
+ }
+
+ vc->lastipts = MP_NOPTS_VALUE;
+ vc->lastframeipts = MP_NOPTS_VALUE;
+
+ if (pix_fmt == PIX_FMT_NONE)
+ goto error; /* imgfmt2pixfmt already prints something */
+
+ vc->stream = encode_lavc_alloc_stream(vo->encode_lavc_ctx,
+ AVMEDIA_TYPE_VIDEO);
+ vc->stream->sample_aspect_ratio = vc->stream->codec->sample_aspect_ratio =
+ aspect;
+ vc->stream->codec->width = width;
+ vc->stream->codec->height = height;
+ vc->stream->codec->pix_fmt = pix_fmt;
+
+ encode_lavc_set_csp(vo->encode_lavc_ctx, vc->stream, vc->colorspace.format);
+ encode_lavc_set_csp_levels(vo->encode_lavc_ctx, vc->stream, vc->colorspace.levels_out);
+
+ if (encode_lavc_open_codec(vo->encode_lavc_ctx, vc->stream) < 0)
+ goto error;
+
+ vc->colorspace.format = encode_lavc_get_csp(vo->encode_lavc_ctx, vc->stream);
+ vc->colorspace.levels_out = encode_lavc_get_csp_levels(vo->encode_lavc_ctx, vc->stream);
+
+ vc->buffer_size = 6 * width * height + 200;
+ if (vc->buffer_size < FF_MIN_BUFFER_SIZE)
+ vc->buffer_size = FF_MIN_BUFFER_SIZE;
+ if (vc->buffer_size < sizeof(AVPicture))
+ vc->buffer_size = sizeof(AVPicture);
+
+ vc->buffer = talloc_size(vc, vc->buffer_size);
+
+ vc->lastimg = alloc_mpi(width, height, format);
+
+ // palette hack
+ if (vc->lastimg->imgfmt == IMGFMT_RGB8 ||
+ vc->lastimg->imgfmt == IMGFMT_BGR8)
+ vc->lastimg->planes[1] = talloc_zero_size(vc, 1024);
+
+ return 0;
+
+error:
+ uninit(vo);
+ return -1;
+}
+
+static int query_format(struct vo *vo, uint32_t format)
+{
+ enum PixelFormat pix_fmt = imgfmt2pixfmt(format);
+
+ if (!vo->encode_lavc_ctx)
+ return 0;
+
+ if (!encode_lavc_supports_pixfmt(vo->encode_lavc_ctx, pix_fmt))
+ return 0;
+
+ return
+ VFCAP_CSP_SUPPORTED |
+ // we can do it
+ VFCAP_CSP_SUPPORTED_BY_HW |
+ // we don't convert colorspaces here
+ VFCAP_OSD |
+ // we have OSD
+ VOCAP_NOSLICES;
+ // we don't use slices
+}
+
+static void write_packet(struct vo *vo, int size, AVPacket *packet)
+{
+ struct priv *vc = vo->priv;
+
+ if (size < 0) {
+ mp_msg(MSGT_ENCODE, MSGL_ERR, "vo-lavc: error encoding\n");
+ return;
+ }
+
+ if (size > 0) {
+ packet->stream_index = vc->stream->index;
+ if (packet->pts != AV_NOPTS_VALUE) {
+ packet->pts = av_rescale_q(packet->pts,
+ vc->stream->codec->time_base,
+ vc->stream->time_base);
+ } else {
+ mp_msg(MSGT_ENCODE, MSGL_WARN, "vo-lavc: codec did not provide pts\n");
+ packet->pts = av_rescale_q(vc->lastipts, vc->worst_time_base,
+ vc->stream->time_base);
+ }
+ if (packet->dts != AV_NOPTS_VALUE) {
+ packet->dts = av_rescale_q(packet->dts,
+ vc->stream->codec->time_base,
+ vc->stream->time_base);
+ }
+ if (packet->duration > 0) {
+ packet->duration = av_rescale_q(packet->duration,
+ vc->stream->codec->time_base,
+ vc->stream->time_base);
+ } else {
+ // HACK: libavformat calculates dts wrong if the initial packet
+ // duration is not set, but ONLY if the time base is "high" and if we
+ // have b-frames!
+ if (!packet->duration)
+ if (!vc->have_first_packet)
+ if (vc->stream->codec->has_b_frames
+ || vc->stream->codec->max_b_frames)
+ if (vc->stream->time_base.num * 1000LL <=
+ vc->stream->time_base.den)
+ packet->duration = FFMAX(1, av_rescale_q(1,
+ vc->stream->codec->time_base, vc->stream->time_base));
+ }
+
+ if (encode_lavc_write_frame(vo->encode_lavc_ctx, packet) < 0) {
+ mp_msg(MSGT_ENCODE, MSGL_ERR, "vo-lavc: error writing\n");
+ return;
+ }
+
+ vc->have_first_packet = 1;
+ }
+}
+
+static int encode_video(struct vo *vo, AVFrame *frame, AVPacket *packet)
+{
+ struct priv *vc = vo->priv;
+ if (encode_lavc_oformat_flags(vo->encode_lavc_ctx) & AVFMT_RAWPICTURE) {
+ if (!frame)
+ return 0;
+ memcpy(vc->buffer, frame, sizeof(AVPicture));
+ mp_msg(MSGT_ENCODE, MSGL_DBG2, "vo-lavc: got pts %f\n",
+ frame->pts * (double) vc->stream->codec->time_base.num /
+ (double) vc->stream->codec->time_base.den);
+ packet->size = sizeof(AVPicture);
+ return packet->size;
+ } else {
+ int got_packet = 0;
+ int status = avcodec_encode_video2(vc->stream->codec, packet,
+ frame, &got_packet);
+ int size = (status < 0) ? status : got_packet ? packet->size : 0;
+
+ if (frame)
+ mp_msg(MSGT_ENCODE, MSGL_DBG2, "vo-lavc: got pts %f; out size: %d\n",
+ frame->pts * (double) vc->stream->codec->time_base.num /
+ (double) vc->stream->codec->time_base.den, size);
+
+ encode_lavc_write_stats(vo->encode_lavc_ctx, vc->stream);
+ return size;
+ }
+}
+
+static void draw_image(struct vo *vo, mp_image_t *mpi, double pts)
+{
+ struct priv *vc = vo->priv;
+ struct encode_lavc_context *ectx = vo->encode_lavc_ctx;
+ int i, size;
+ AVFrame *frame;
+ AVCodecContext *avc;
+ int64_t frameipts;
+ double nextpts;
+
+ if (!vc)
+ return;
+ if (!encode_lavc_start(ectx)) {
+ mp_msg(MSGT_ENCODE, MSGL_WARN, "vo-lavc: NOTE: skipped initial video frame (probably because audio is not there yet)\n");
+ return;
+ }
+ if (pts == MP_NOPTS_VALUE) {
+ if (mpi)
+ mp_msg(MSGT_ENCODE, MSGL_WARN, "vo-lavc: frame without pts, please report; synthesizing pts instead\n");
+ pts = vc->expected_next_pts;
+ }
+
+ avc = vc->stream->codec;
+
+ if (vc->worst_time_base.den == 0) {
+ //if (avc->time_base.num / avc->time_base.den >= vc->stream->time_base.num / vc->stream->time_base.den)
+ if (avc->time_base.num * (double) vc->stream->time_base.den >=
+ vc->stream->time_base.num * (double) avc->time_base.den) {
+ mp_msg(MSGT_ENCODE, MSGL_V, "vo-lavc: NOTE: using codec time base "
+ "(%d/%d) for frame dropping; the stream base (%d/%d) is "
+ "not worse.\n", (int)avc->time_base.num,
+ (int)avc->time_base.den, (int)vc->stream->time_base.num,
+ (int)vc->stream->time_base.den);
+ vc->worst_time_base = avc->time_base;
+ vc->worst_time_base_is_stream = 0;
+ } else {
+ mp_msg(MSGT_ENCODE, MSGL_WARN, "vo-lavc: NOTE: not using codec time "
+ "base (%d/%d) for frame dropping; the stream base (%d/%d) "
+ "is worse.\n", (int)avc->time_base.num,
+ (int)avc->time_base.den, (int)vc->stream->time_base.num,
+ (int)vc->stream->time_base.den);
+ vc->worst_time_base = vc->stream->time_base;
+ vc->worst_time_base_is_stream = 1;
+ }
+
+ // NOTE: we use the following "axiom" of av_rescale_q:
+ // if time base A is worse than time base B, then
+ // av_rescale_q(av_rescale_q(x, A, B), B, A) == x
+ // this can be proven as long as av_rescale_q rounds to nearest, which
+ // it currently does
+
+ // av_rescale_q(x, A, B) * B = "round x*A to nearest multiple of B"
+ // and:
+ // av_rescale_q(av_rescale_q(x, A, B), B, A) * A
+ // == "round av_rescale_q(x, A, B)*B to nearest multiple of A"
+ // == "round 'round x*A to nearest multiple of B' to nearest multiple of A"
+ //
+ // assume this fails. Then there is a value of x*A, for which the
+ // nearest multiple of B is outside the range [(x-0.5)*A, (x+0.5)*A[.
+ // Absurd, as this range MUST contain at least one multiple of B.
+ }
+
+ double timeunit = (double)vc->worst_time_base.num / vc->worst_time_base.den;
+
+ double outpts;
+ if (ectx->options->rawts)
+ outpts = pts;
+ else if (ectx->options->copyts) {
+ // fix the discontinuity pts offset
+ nextpts = pts;
+ if (ectx->discontinuity_pts_offset == MP_NOPTS_VALUE) {
+ ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts;
+ }
+ else if (fabs(nextpts + ectx->discontinuity_pts_offset - ectx->next_in_pts) > 30) {
+ mp_msg(MSGT_ENCODE, MSGL_WARN,
+ "vo-lavc: detected an unexpected discontinuity (pts jumped by "
+ "%f seconds)\n",
+ nextpts + ectx->discontinuity_pts_offset - ectx->next_in_pts);
+ ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts;
+ }
+
+ outpts = pts + ectx->discontinuity_pts_offset;
+ }
+ else {
+ // adjust pts by knowledge of audio pts vs audio playback time
+ double duration = 0;
+ if (ectx->last_video_in_pts != MP_NOPTS_VALUE)
+ duration = pts - ectx->last_video_in_pts;
+ if (duration < 0)
+ duration = timeunit; // XXX warn about discontinuity?
+ outpts = vc->lastpts + duration;
+ if (ectx->audio_pts_offset != MP_NOPTS_VALUE) {
+ double adj = outpts - pts - ectx->audio_pts_offset;
+ adj = FFMIN(adj, duration * 0.1);
+ adj = FFMAX(adj, -duration * 0.1);
+ outpts -= adj;
+ }
+ }
+ vc->lastpts = outpts;
+ ectx->last_video_in_pts = pts;
+ frameipts = floor((outpts + encode_lavc_getoffset(ectx, vc->stream))
+ / timeunit + 0.5);
+
+ // calculate expected pts of next video frame
+ vc->expected_next_pts = pts + timeunit;
+
+ if (!ectx->options->rawts && ectx->options->copyts) {
+ // set next allowed output pts value
+ nextpts = vc->expected_next_pts + ectx->discontinuity_pts_offset;
+ if (nextpts > ectx->next_in_pts)
+ ectx->next_in_pts = nextpts;
+ }
+
+ // never-drop mode
+ if (ectx->options->neverdrop && frameipts <= vc->lastipts) {
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "vo-lavc: -oneverdrop increased pts by %d\n",
+ (int) (vc->lastipts - frameipts + 1));
+ frameipts = vc->lastipts + 1;
+ vc->lastpts = frameipts * timeunit - encode_lavc_getoffset(ectx, vc->stream);
+ }
+
+ if (vc->lastipts != MP_NOPTS_VALUE) {
+ frame = avcodec_alloc_frame();
+
+ // we have a valid image in lastimg
+ while (vc->lastipts < frameipts) {
+ int64_t thisduration = vc->harddup ? 1 : (frameipts - vc->lastipts);
+ AVPacket packet;
+
+ avcodec_get_frame_defaults(frame);
+
+ // this is a nop, unless the worst time base is the STREAM time base
+ frame->pts = av_rescale_q(vc->lastipts, vc->worst_time_base,
+ avc->time_base);
+
+ for (i = 0; i < 4; i++) {
+ frame->data[i] = vc->lastimg->planes[i];
+ frame->linesize[i] = vc->lastimg->stride[i];
+ }
+ frame->quality = avc->global_quality;
+
+ av_init_packet(&packet);
+ packet.data = vc->buffer;
+ packet.size = vc->buffer_size;
+ size = encode_video(vo, frame, &packet);
+ write_packet(vo, size, &packet);
+
+ vc->lastipts += thisduration;
+ ++vc->lastdisplaycount;
+ }
+
+ avcodec_free_frame(&frame);
+ }
+
+ if (!mpi) {
+ // finish encoding
+ do {
+ AVPacket packet;
+ av_init_packet(&packet);
+ packet.data = vc->buffer;
+ packet.size = vc->buffer_size;
+ size = encode_video(vo, NULL, &packet);
+ write_packet(vo, size, &packet);
+ } while (size > 0);
+ } else {
+ if (frameipts >= vc->lastframeipts) {
+ if (vc->lastframeipts != MP_NOPTS_VALUE && vc->lastdisplaycount != 1)
+ mp_msg(MSGT_ENCODE, MSGL_INFO,
+ "vo-lavc: Frame at pts %d got displayed %d times\n",
+ (int) vc->lastframeipts, vc->lastdisplaycount);
+ copy_mpi(vc->lastimg, mpi);
+ vc->lastimg_wants_osd = true;
+
+ // palette hack
+ if (vc->lastimg->imgfmt == IMGFMT_RGB8 ||
+ vc->lastimg->imgfmt == IMGFMT_BGR8)
+ memcpy(vc->lastimg->planes[1], mpi->planes[1], 1024);
+
+ vc->lastframeipts = vc->lastipts = frameipts;
+ if (ectx->options->rawts && vc->lastipts < 0) {
+ mp_msg(MSGT_ENCODE, MSGL_ERR, "vo-lavc: why does this happen? DEBUG THIS! vc->lastipts = %lld\n", (long long) vc->lastipts);
+ vc->lastipts = -1;
+ }
+ vc->lastdisplaycount = 0;
+ } else {
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "vo-lavc: Frame at pts %d got dropped "
+ "entirely because pts went backwards\n", (int) frameipts);
+ vc->lastimg_wants_osd = false;
+ }
+ }
+}
+
+static void flip_page_timed(struct vo *vo, unsigned int pts_us, int duration)
+{
+}
+
+static void check_events(struct vo *vo)
+{
+}
+
+static void draw_osd(struct vo *vo, struct osd_state *osd)
+{
+ struct priv *vc = vo->priv;
+
+ if (vc->lastimg && vc->lastimg_wants_osd) {
+ struct aspect_data asp = vo->aspdat;
+ double sar = (double)asp.orgw / asp.orgh;
+ double dar = (double)asp.prew / asp.preh;
+
+ struct mp_osd_res dim = {
+ .w = asp.orgw,
+ .h = asp.orgh,
+ .display_par = sar / dar,
+ .video_par = dar / sar,
+ };
+
+ mp_image_set_colorspace_details(vc->lastimg, &vc->colorspace);
+
+ osd_draw_on_image(osd, dim, osd->vo_pts, OSD_DRAW_SUB_ONLY, vc->lastimg);
+ }
+}
+
+static int control(struct vo *vo, uint32_t request, void *data)
+{
+ struct priv *vc = vo->priv;
+ switch (request) {
+ case VOCTRL_QUERY_FORMAT:
+ return query_format(vo, *((uint32_t *)data));
+ case VOCTRL_DRAW_IMAGE:
+ draw_image(vo, (mp_image_t *)data, vo->next_pts);
+ return 0;
+ case VOCTRL_SET_YUV_COLORSPACE:
+ vc->colorspace = *(struct mp_csp_details *)data;
+ if (vc->stream) {
+ encode_lavc_set_csp(vo->encode_lavc_ctx, vc->stream, vc->colorspace.format);
+ encode_lavc_set_csp_levels(vo->encode_lavc_ctx, vc->stream, vc->colorspace.levels_out);
+ vc->colorspace.format = encode_lavc_get_csp(vo->encode_lavc_ctx, vc->stream);
+ vc->colorspace.levels_out = encode_lavc_get_csp_levels(vo->encode_lavc_ctx, vc->stream);
+ }
+ return 1;
+ case VOCTRL_GET_YUV_COLORSPACE:
+ *(struct mp_csp_details *)data = vc->colorspace;
+ return 1;
+ }
+ return VO_NOTIMPL;
+}
+
+const struct vo_driver video_out_lavc = {
+ .is_new = true,
+ .buffer_frames = false,
+ .info = &(const struct vo_info_s){
+ "video encoding using libavcodec",
+ "lavc",
+ "Nicolas George <george@nsup.org>, Rudolf Polzer <divVerent@xonotic.org>",
+ ""
+ },
+ .preinit = preinit,
+ .config = config,
+ .control = control,
+ .uninit = uninit,
+ .check_events = check_events,
+ .draw_osd = draw_osd,
+ .flip_page_timed = flip_page_timed,
+};
+
+// vim: sw=4 ts=4 et tw=80
diff --git a/video/out/vo_null.c b/video/out/vo_null.c
new file mode 100644
index 0000000000..1f307f7f5b
--- /dev/null
+++ b/video/out/vo_null.c
@@ -0,0 +1,103 @@
+/*
+ * based on video_out_null.c from mpeg2dec
+ *
+ * Copyright (C) Aaron Holtzman - June 2000
+ *
+ * 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 <errno.h>
+#include "config.h"
+#include "mp_msg.h"
+#include "video_out.h"
+#include "libmpcodecs/vfcap.h"
+#include "libmpcodecs/mp_image.h"
+
+static int draw_slice(struct vo *vo, uint8_t *image[], int stride[],
+ int w, int h, int x, int y)
+{
+ return 0;
+}
+
+static void draw_osd(struct vo *vo, struct osd_state *osd)
+{
+}
+
+static void flip_page(struct vo *vo)
+{
+}
+
+static int query_format(struct vo *vo, uint32_t format)
+{
+ if (IMGFMT_IS_HWACCEL(format))
+ return 0;
+ return VFCAP_CSP_SUPPORTED;
+}
+
+static int config(struct vo *vo, uint32_t width, uint32_t height,
+ uint32_t d_width, uint32_t d_height, uint32_t flags,
+ uint32_t format)
+{
+ return 0;
+}
+
+static void uninit(struct vo *vo)
+{
+}
+
+static void check_events(struct vo *vo)
+{
+}
+
+static int preinit(struct vo *vo, const char *arg)
+{
+ if (arg) {
+ mp_tmsg(MSGT_VO, MSGL_WARN, "[VO_NULL] Unknown subdevice: %s.\n", arg);
+ return ENOSYS;
+ }
+ return 0;
+}
+
+static int control(struct vo *vo, uint32_t request, void *data)
+{
+ switch (request) {
+ case VOCTRL_QUERY_FORMAT:
+ return query_format(vo, *((uint32_t *)data));
+ }
+ return VO_NOTIMPL;
+}
+
+const struct vo_driver video_out_null = {
+ .is_new = false,
+ .info = &(const vo_info_t) {
+ "Null video output",
+ "null",
+ "Aaron Holtzman <aholtzma@ess.engr.uvic.ca>",
+ ""
+ },
+ .preinit = preinit,
+ .config = config,
+ .control = control,
+ .draw_slice = draw_slice,
+ .draw_osd = draw_osd,
+ .flip_page = flip_page,
+ .check_events = check_events,
+ .uninit = uninit,
+};
diff --git a/video/out/vo_opengl.c b/video/out/vo_opengl.c
new file mode 100644
index 0000000000..7b5289838f
--- /dev/null
+++ b/video/out/vo_opengl.c
@@ -0,0 +1,2419 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <stdbool.h>
+#include <assert.h>
+#include "config.h"
+
+#include <libavutil/common.h>
+
+#ifdef CONFIG_LCMS2
+#include <lcms2.h>
+#include "stream/stream.h"
+#endif
+
+#include "talloc.h"
+#include "mpcommon.h"
+#include "bstr.h"
+#include "mp_msg.h"
+#include "subopt-helper.h"
+#include "video_out.h"
+#include "libmpcodecs/vfcap.h"
+#include "libmpcodecs/mp_image.h"
+#include "geometry.h"
+#include "sub/sub.h"
+#include "bitmap_packer.h"
+
+#include "gl_common.h"
+#include "gl_osd.h"
+#include "filter_kernels.h"
+#include "aspect.h"
+#include "fastmemcpy.h"
+
+static const char vo_opengl_shaders[] =
+// Generated from libvo/vo_opengl_shaders.glsl
+#include "libvo/vo_opengl_shaders.h"
+;
+
+// Pixel width of 1D lookup textures.
+#define LOOKUP_TEXTURE_SIZE 256
+
+// Texture units 0-2 are used by the video, with unit 0 for free use.
+// Units 3-4 are used for scaler LUTs.
+#define TEXUNIT_SCALERS 3
+#define TEXUNIT_3DLUT 5
+#define TEXUNIT_DITHER 6
+
+// lscale/cscale arguments that map directly to shader filter routines.
+// Note that the convolution filters are not included in this list.
+static const char *fixed_scale_filters[] = {
+ "bilinear",
+ "bicubic_fast",
+ "sharpen3",
+ "sharpen5",
+ NULL
+};
+
+struct lut_tex_format {
+ int pixels;
+ GLint internal_format;
+ GLenum format;
+};
+
+// Indexed with filter_kernel->size.
+// This must match the weightsN functions in the shader.
+// Each entry uses (size+3)/4 pixels per LUT entry, and size/pixels components
+// per pixel.
+struct lut_tex_format lut_tex_formats[] = {
+ [2] = {1, GL_RG16F, GL_RG},
+ [4] = {1, GL_RGBA16F, GL_RGBA},
+ [6] = {2, GL_RGB16F, GL_RGB},
+ [8] = {2, GL_RGBA16F, GL_RGBA},
+ [12] = {3, GL_RGBA16F, GL_RGBA},
+ [16] = {4, GL_RGBA16F, GL_RGBA},
+};
+
+// must be sorted, and terminated with 0
+static const int filter_sizes[] = {2, 4, 6, 8, 12, 16, 0};
+
+struct vertex {
+ float position[2];
+ uint8_t color[4];
+ float texcoord[2];
+};
+
+#define VERTEX_ATTRIB_POSITION 0
+#define VERTEX_ATTRIB_COLOR 1
+#define VERTEX_ATTRIB_TEXCOORD 2
+
+// 2 triangles primitives per quad = 6 vertices per quad
+// (GL_QUAD is deprecated, strips can't be used with OSD image lists)
+#define VERTICES_PER_QUAD 6
+
+struct texplane {
+ int shift_x, shift_y;
+ GLuint gl_texture;
+ int gl_buffer;
+ int buffer_size;
+ void *buffer_ptr;
+};
+
+struct scaler {
+ int index;
+ const char *name;
+ float params[2];
+ struct filter_kernel *kernel;
+ GLuint gl_lut;
+ const char *lut_name;
+
+ // kernel points here
+ struct filter_kernel kernel_storage;
+};
+
+struct fbotex {
+ GLuint fbo;
+ GLuint texture;
+ int tex_w, tex_h; // size of .texture
+ int vp_w, vp_h; // viewport of fbo / used part of the texture
+};
+
+struct gl_priv {
+ struct vo *vo;
+ MPGLContext *glctx;
+ GL *gl;
+
+ int use_indirect;
+ int use_gamma;
+ int use_srgb;
+ int use_scale_sep;
+ int use_fancy_downscaling;
+ int use_lut_3d;
+ int use_npot;
+ int use_pbo;
+ int use_glFinish;
+ int use_gl_debug;
+ int allow_sw;
+
+ int dither_depth;
+ int swap_interval;
+ GLint fbo_format;
+ int stereo_mode;
+ int osd_color;
+
+ struct gl_priv *defaults;
+ struct gl_priv *orig_cmdline;
+
+ GLuint vertex_buffer;
+ GLuint vao;
+
+ GLuint osd_programs[SUBBITMAP_COUNT];
+ GLuint indirect_program, scale_sep_program, final_program;
+
+ struct mpgl_osd *osd;
+
+ GLuint lut_3d_texture;
+ int lut_3d_w, lut_3d_h, lut_3d_d;
+ void *lut_3d_data;
+
+ GLuint dither_texture;
+ float dither_quantization;
+ float dither_multiply;
+ int dither_size;
+
+ uint32_t image_width;
+ uint32_t image_height;
+ uint32_t image_format;
+ int texture_width;
+ int texture_height;
+
+ bool is_yuv;
+ bool is_linear_rgb;
+
+ // per pixel (full pixel when packed, each component when planar)
+ int plane_bytes;
+ int plane_bits;
+ int component_bits;
+
+ GLint gl_internal_format;
+ GLenum gl_format;
+ GLenum gl_type;
+
+ int plane_count;
+ struct texplane planes[3];
+
+ struct fbotex indirect_fbo; // RGB target
+ struct fbotex scale_sep_fbo; // first pass when doing 2 pass scaling
+
+ // state for luma (0) and chroma (1) scalers
+ struct scaler scalers[2];
+ // luma scaler parameters (the same are used for chroma)
+ float scaler_params[2];
+
+ struct mp_csp_details colorspace;
+ struct mp_csp_equalizer video_eq;
+
+ int mpi_flipped;
+ int vo_flipped;
+
+ struct mp_rect src_rect; // displayed part of the source video
+ struct mp_rect dst_rect; // video rectangle on output window
+ struct mp_osd_res osd_rect; // OSD size/margins
+ int vp_x, vp_y, vp_w, vp_h; // GL viewport
+
+ int frames_rendered;
+
+ void *scratch;
+};
+
+struct fmt_entry {
+ int mp_format;
+ GLint internal_format;
+ GLenum format;
+ int component_bits;
+ GLenum type;
+};
+
+static const struct fmt_entry mp_to_gl_formats[] = {
+ {IMGFMT_RGB48NE, GL_RGB16, GL_RGB, 16, GL_UNSIGNED_SHORT},
+ {IMGFMT_RGB24, GL_RGB, GL_RGB, 8, GL_UNSIGNED_BYTE},
+ {IMGFMT_RGBA, GL_RGBA, GL_RGBA, 8, GL_UNSIGNED_BYTE},
+ {IMGFMT_RGB15, GL_RGBA, GL_RGBA, 5, GL_UNSIGNED_SHORT_1_5_5_5_REV},
+ {IMGFMT_RGB16, GL_RGB, GL_RGB, 6, GL_UNSIGNED_SHORT_5_6_5_REV},
+ {IMGFMT_BGR15, GL_RGBA, GL_BGRA, 5, GL_UNSIGNED_SHORT_1_5_5_5_REV},
+ {IMGFMT_BGR16, GL_RGB, GL_RGB, 6, GL_UNSIGNED_SHORT_5_6_5},
+ {IMGFMT_BGR24, GL_RGB, GL_BGR, 8, GL_UNSIGNED_BYTE},
+ {IMGFMT_BGRA, GL_RGBA, GL_BGRA, 8, GL_UNSIGNED_BYTE},
+ {0},
+};
+
+static const char *osd_shaders[SUBBITMAP_COUNT] = {
+ [SUBBITMAP_LIBASS] = "frag_osd_libass",
+ [SUBBITMAP_RGBA] = "frag_osd_rgba",
+};
+
+
+static const char help_text[];
+
+static void uninit_rendering(struct gl_priv *p);
+static void delete_shaders(struct gl_priv *p);
+static bool reparse_cmdline(struct gl_priv *p, char *arg);
+
+
+static void default_tex_params(struct GL *gl, GLenum target, GLint filter)
+{
+ gl->TexParameteri(target, GL_TEXTURE_MIN_FILTER, filter);
+ gl->TexParameteri(target, GL_TEXTURE_MAG_FILTER, filter);
+ gl->TexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl->TexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
+static void debug_check_gl(struct gl_priv *p, const char *msg)
+{
+ if (p->use_gl_debug || p->frames_rendered < 5)
+ glCheckError(p->gl, msg);
+}
+
+static void tex_size(struct gl_priv *p, int w, int h, int *texw, int *texh)
+{
+ if (p->use_npot) {
+ *texw = w;
+ *texh = h;
+ } else {
+ *texw = 32;
+ while (*texw < w)
+ *texw *= 2;
+ *texh = 32;
+ while (*texh < h)
+ *texh *= 2;
+ }
+}
+
+static void draw_triangles(struct gl_priv *p, struct vertex *vb, int vert_count)
+{
+ GL *gl = p->gl;
+
+ assert(vert_count % 3 == 0);
+
+ gl->BindBuffer(GL_ARRAY_BUFFER, p->vertex_buffer);
+ gl->BufferData(GL_ARRAY_BUFFER, vert_count * sizeof(struct vertex), vb,
+ GL_DYNAMIC_DRAW);
+ gl->BindBuffer(GL_ARRAY_BUFFER, 0);
+
+ if (gl->BindVertexArray)
+ gl->BindVertexArray(p->vao);
+
+ gl->DrawArrays(GL_TRIANGLES, 0, vert_count);
+
+ if (gl->BindVertexArray)
+ gl->BindVertexArray(0);
+
+ debug_check_gl(p, "after rendering");
+}
+
+// Write a textured quad to a vertex array.
+// va = destination vertex array, VERTICES_PER_QUAD entries will be overwritten
+// x0, y0, x1, y1 = destination coordinates of the quad
+// tx0, ty0, tx1, ty1 = source texture coordinates (usually in pixels)
+// texture_w, texture_h = size of the texture, or an inverse factor
+// color = optional color for all vertices, NULL for opaque white
+// flip = flip vertically
+static void write_quad(struct vertex *va,
+ float x0, float y0, float x1, float y1,
+ float tx0, float ty0, float tx1, float ty1,
+ float texture_w, float texture_h,
+ const uint8_t color[4], bool flip)
+{
+ static const uint8_t white[4] = { 255, 255, 255, 255 };
+
+ if (!color)
+ color = white;
+
+ tx0 /= texture_w;
+ ty0 /= texture_h;
+ tx1 /= texture_w;
+ ty1 /= texture_h;
+
+ if (flip) {
+ float tmp = ty0;
+ ty0 = ty1;
+ ty1 = tmp;
+ }
+
+#define COLOR_INIT {color[0], color[1], color[2], color[3]}
+ va[0] = (struct vertex) { {x0, y0}, COLOR_INIT, {tx0, ty0} };
+ va[1] = (struct vertex) { {x0, y1}, COLOR_INIT, {tx0, ty1} };
+ va[2] = (struct vertex) { {x1, y0}, COLOR_INIT, {tx1, ty0} };
+ va[3] = (struct vertex) { {x1, y1}, COLOR_INIT, {tx1, ty1} };
+ va[4] = va[2];
+ va[5] = va[1];
+#undef COLOR_INIT
+}
+
+static bool fbotex_init(struct gl_priv *p, struct fbotex *fbo, int w, int h)
+{
+ GL *gl = p->gl;
+ bool res = true;
+
+ assert(gl->mpgl_caps & MPGL_CAP_FB);
+ assert(!fbo->fbo);
+ assert(!fbo->texture);
+
+ tex_size(p, w, h, &fbo->tex_w, &fbo->tex_h);
+
+ fbo->vp_w = w;
+ fbo->vp_h = h;
+
+ mp_msg(MSGT_VO, MSGL_V, "[gl] Create FBO: %dx%d\n", fbo->tex_w, fbo->tex_h);
+
+ gl->GenFramebuffers(1, &fbo->fbo);
+ gl->GenTextures(1, &fbo->texture);
+ gl->BindTexture(GL_TEXTURE_2D, fbo->texture);
+ gl->TexImage2D(GL_TEXTURE_2D, 0, p->fbo_format, fbo->tex_w, fbo->tex_h, 0,
+ GL_RGB, GL_UNSIGNED_BYTE, NULL);
+ default_tex_params(gl, GL_TEXTURE_2D, GL_LINEAR);
+ gl->BindFramebuffer(GL_FRAMEBUFFER, fbo->fbo);
+ gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, fbo->texture, 0);
+
+ if (gl->CheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] Error: framebuffer completeness "
+ "check failed!\n");
+ res = false;
+ }
+
+ gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ debug_check_gl(p, "after creating framebuffer & associated texture");
+
+ return res;
+}
+
+static void fbotex_uninit(struct gl_priv *p, struct fbotex *fbo)
+{
+ GL *gl = p->gl;
+
+ if (gl->mpgl_caps & MPGL_CAP_FB) {
+ gl->DeleteFramebuffers(1, &fbo->fbo);
+ gl->DeleteTextures(1, &fbo->texture);
+ *fbo = (struct fbotex) {0};
+ }
+}
+
+static void matrix_ortho2d(float m[3][3], float x0, float x1,
+ float y0, float y1)
+{
+ memset(m, 0, 9 * sizeof(float));
+ m[0][0] = 2.0f / (x1 - x0);
+ m[1][1] = 2.0f / (y1 - y0);
+ m[2][0] = -(x1 + x0) / (x1 - x0);
+ m[2][1] = -(y1 + y0) / (y1 - y0);
+ m[2][2] = 1.0f;
+}
+
+static void update_uniforms(struct gl_priv *p, GLuint program)
+{
+ GL *gl = p->gl;
+ GLint loc;
+
+ if (program == 0)
+ return;
+
+ gl->UseProgram(program);
+
+ struct mp_csp_params cparams = {
+ .colorspace = p->colorspace,
+ .input_bits = p->plane_bits,
+ .texture_bits = (p->plane_bits + 7) & ~7,
+ };
+ mp_csp_copy_equalizer_values(&cparams, &p->video_eq);
+
+ loc = gl->GetUniformLocation(program, "transform");
+ if (loc >= 0) {
+ float matrix[3][3];
+ matrix_ortho2d(matrix, 0, p->vp_w, p->vp_h, 0);
+ gl->UniformMatrix3fv(loc, 1, GL_FALSE, &matrix[0][0]);
+ }
+
+ loc = gl->GetUniformLocation(program, "colormatrix");
+ if (loc >= 0) {
+ float yuv2rgb[3][4] = {{0}};
+ if (p->is_yuv)
+ mp_get_yuv2rgb_coeffs(&cparams, yuv2rgb);
+ gl->UniformMatrix4x3fv(loc, 1, GL_TRUE, &yuv2rgb[0][0]);
+ }
+
+ gl->Uniform3f(gl->GetUniformLocation(program, "inv_gamma"),
+ 1.0 / cparams.rgamma,
+ 1.0 / cparams.ggamma,
+ 1.0 / cparams.bgamma);
+
+ for (int n = 0; n < p->plane_count; n++) {
+ char textures_n[32];
+ char textures_size_n[32];
+ snprintf(textures_n, sizeof(textures_n), "textures[%d]", n);
+ snprintf(textures_size_n, sizeof(textures_size_n), "textures_size[%d]", n);
+
+ gl->Uniform1i(gl->GetUniformLocation(program, textures_n), n);
+ gl->Uniform2f(gl->GetUniformLocation(program, textures_size_n),
+ p->texture_width >> p->planes[n].shift_x,
+ p->texture_height >> p->planes[n].shift_y);
+ }
+
+ gl->Uniform2f(gl->GetUniformLocation(program, "dither_size"),
+ p->dither_size, p->dither_size);
+
+ gl->Uniform1i(gl->GetUniformLocation(program, "lut_3d"), TEXUNIT_3DLUT);
+
+ for (int n = 0; n < 2; n++) {
+ const char *lut = p->scalers[n].lut_name;
+ if (lut)
+ gl->Uniform1i(gl->GetUniformLocation(program, lut),
+ TEXUNIT_SCALERS + n);
+ }
+
+ gl->Uniform1i(gl->GetUniformLocation(program, "dither"), TEXUNIT_DITHER);
+ gl->Uniform1f(gl->GetUniformLocation(program, "dither_quantization"),
+ p->dither_quantization);
+ gl->Uniform1f(gl->GetUniformLocation(program, "dither_multiply"),
+ p->dither_multiply);
+
+ float sparam1 = p->scaler_params[0];
+ gl->Uniform1f(gl->GetUniformLocation(program, "filter_param1"),
+ isnan(sparam1) ? 0.5f : sparam1);
+
+ loc = gl->GetUniformLocation(program, "osd_color");
+ if (loc >= 0) {
+ int r = (p->osd_color >> 16) & 0xff;
+ int g = (p->osd_color >> 8) & 0xff;
+ int b = p->osd_color & 0xff;
+ int a = 0xff - (p->osd_color >> 24);
+ gl->Uniform4f(loc, r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f);
+ }
+
+ gl->UseProgram(0);
+
+ debug_check_gl(p, "update_uniforms()");
+}
+
+static void update_all_uniforms(struct gl_priv *p)
+{
+ for (int n = 0; n < SUBBITMAP_COUNT; n++)
+ update_uniforms(p, p->osd_programs[n]);
+ update_uniforms(p, p->indirect_program);
+ update_uniforms(p, p->scale_sep_program);
+ update_uniforms(p, p->final_program);
+}
+
+#define SECTION_HEADER "#!section "
+
+static char *get_section(void *talloc_ctx, struct bstr source,
+ const char *section)
+{
+ char *res = talloc_strdup(talloc_ctx, "");
+ bool copy = false;
+ while (source.len) {
+ struct bstr line = bstr_strip_linebreaks(bstr_getline(source, &source));
+ if (bstr_eatstart(&line, bstr0(SECTION_HEADER))) {
+ copy = bstrcmp0(line, section) == 0;
+ } else if (copy) {
+ res = talloc_asprintf_append_buffer(res, "%.*s\n", BSTR_P(line));
+ }
+ }
+ return res;
+}
+
+static char *t_concat(void *talloc_ctx, const char *s1, const char *s2)
+{
+ return talloc_asprintf(talloc_ctx, "%s%s", s1, s2);
+}
+
+static GLuint create_shader(GL *gl, GLenum type, const char *header,
+ const char *source)
+{
+ void *tmp = talloc_new(NULL);
+ const char *full_source = t_concat(tmp, header, source);
+
+ GLuint shader = gl->CreateShader(type);
+ gl->ShaderSource(shader, 1, &full_source, NULL);
+ gl->CompileShader(shader);
+ GLint status;
+ gl->GetShaderiv(shader, GL_COMPILE_STATUS, &status);
+ GLint log_length;
+ gl->GetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
+
+ int pri = status ? (log_length > 1 ? MSGL_V : MSGL_DBG2) : MSGL_ERR;
+ const char *typestr = type == GL_VERTEX_SHADER ? "vertex" : "fragment";
+ if (mp_msg_test(MSGT_VO, pri)) {
+ mp_msg(MSGT_VO, pri, "[gl] %s shader source:\n", typestr);
+ mp_log_source(MSGT_VO, pri, full_source);
+ }
+ if (log_length > 1) {
+ GLchar *log = talloc_zero_size(tmp, log_length + 1);
+ gl->GetShaderInfoLog(shader, log_length, NULL, log);
+ mp_msg(MSGT_VO, pri, "[gl] %s shader compile log (status=%d):\n%s\n",
+ typestr, status, log);
+ }
+
+ talloc_free(tmp);
+
+ return shader;
+}
+
+static void prog_create_shader(GL *gl, GLuint program, GLenum type,
+ const char *header, const char *source)
+{
+ GLuint shader = create_shader(gl, type, header, source);
+ gl->AttachShader(program, shader);
+ gl->DeleteShader(shader);
+}
+
+static void link_shader(GL *gl, GLuint program)
+{
+ gl->LinkProgram(program);
+ GLint status;
+ gl->GetProgramiv(program, GL_LINK_STATUS, &status);
+ GLint log_length;
+ gl->GetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
+
+ int pri = status ? (log_length > 1 ? MSGL_V : MSGL_DBG2) : MSGL_ERR;
+ if (mp_msg_test(MSGT_VO, pri)) {
+ GLchar *log = talloc_zero_size(NULL, log_length + 1);
+ gl->GetProgramInfoLog(program, log_length, NULL, log);
+ mp_msg(MSGT_VO, pri, "[gl] shader link log (status=%d): %s\n",
+ status, log);
+ talloc_free(log);
+ }
+}
+
+static void bind_attrib_locs(GL *gl, GLuint program)
+{
+ gl->BindAttribLocation(program, VERTEX_ATTRIB_POSITION, "vertex_position");
+ gl->BindAttribLocation(program, VERTEX_ATTRIB_COLOR, "vertex_color");
+ gl->BindAttribLocation(program, VERTEX_ATTRIB_TEXCOORD, "vertex_texcoord");
+}
+
+static GLuint create_program(GL *gl, const char *name, const char *header,
+ const char *vertex, const char *frag)
+{
+ mp_msg(MSGT_VO, MSGL_V, "[gl] compiling shader program '%s'\n", name);
+ mp_msg(MSGT_VO, MSGL_V, "[gl] header:\n");
+ mp_log_source(MSGT_VO, MSGL_V, header);
+ GLuint prog = gl->CreateProgram();
+ prog_create_shader(gl, prog, GL_VERTEX_SHADER, header, vertex);
+ prog_create_shader(gl, prog, GL_FRAGMENT_SHADER, header, frag);
+ bind_attrib_locs(gl, prog);
+ link_shader(gl, prog);
+ return prog;
+}
+
+static void shader_def(char **shader, const char *name,
+ const char *value)
+{
+ *shader = talloc_asprintf_append(*shader, "#define %s %s\n", name, value);
+}
+
+static void shader_def_opt(char **shader, const char *name, bool b)
+{
+ if (b)
+ shader_def(shader, name, "1");
+}
+
+static void shader_setup_scaler(char **shader, struct scaler *scaler, int pass)
+{
+ const char *target = scaler->index == 0 ? "SAMPLE_L" : "SAMPLE_C";
+ if (!scaler->kernel) {
+ *shader = talloc_asprintf_append(*shader, "#define %s sample_%s\n",
+ target, scaler->name);
+ } else {
+ int size = scaler->kernel->size;
+ if (pass != -1) {
+ // The direction/pass assignment is rather arbitrary, but fixed in
+ // other parts of the code (like FBO setup).
+ const char *direction = pass == 0 ? "0, 1" : "1, 0";
+ *shader = talloc_asprintf_append(*shader, "#define %s(p0, p1, p2) "
+ "sample_convolution_sep%d(vec2(%s), %s, p0, p1, p2)\n",
+ target, size, direction, scaler->lut_name);
+ } else {
+ *shader = talloc_asprintf_append(*shader, "#define %s(p0, p1, p2) "
+ "sample_convolution%d(%s, p0, p1, p2)\n",
+ target, size, scaler->lut_name);
+ }
+ }
+}
+
+// return false if RGB or 4:4:4 YUV
+static bool input_is_subsampled(struct gl_priv *p)
+{
+ for (int i = 0; i < p->plane_count; i++)
+ if (p->planes[i].shift_x || p->planes[i].shift_y)
+ return true;
+ return false;
+}
+
+static void compile_shaders(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+
+ delete_shaders(p);
+
+ void *tmp = talloc_new(NULL);
+
+ struct bstr src = bstr0(vo_opengl_shaders);
+ char *vertex_shader = get_section(tmp, src, "vertex_all");
+ char *shader_prelude = get_section(tmp, src, "prelude");
+ char *s_video = get_section(tmp, src, "frag_video");
+
+ char *header = talloc_asprintf(tmp, "#version %d\n%s", gl->glsl_version,
+ shader_prelude);
+
+ char *header_osd = talloc_strdup(tmp, header);
+ shader_def_opt(&header_osd, "USE_OSD_LINEAR_CONV", p->use_srgb &&
+ !p->use_lut_3d);
+ shader_def_opt(&header_osd, "USE_OSD_3DLUT", p->use_lut_3d);
+ shader_def_opt(&header_osd, "USE_OSD_SRGB", p->use_srgb);
+
+ for (int n = 0; n < SUBBITMAP_COUNT; n++) {
+ const char *name = osd_shaders[n];
+ if (name) {
+ char *s_osd = get_section(tmp, src, name);
+ p->osd_programs[n] =
+ create_program(gl, name, header_osd, vertex_shader, s_osd);
+ }
+ }
+
+ char *header_conv = talloc_strdup(tmp, "");
+ char *header_final = talloc_strdup(tmp, "");
+ char *header_sep = NULL;
+
+ bool convert_input_to_linear = !p->is_linear_rgb
+ && (p->use_srgb || p->use_lut_3d);
+
+ shader_def_opt(&header_conv, "USE_PLANAR", p->plane_count > 1);
+ shader_def_opt(&header_conv, "USE_GBRP", p->image_format == IMGFMT_GBRP);
+ shader_def_opt(&header_conv, "USE_YGRAY", p->is_yuv && p->plane_count == 1);
+ shader_def_opt(&header_conv, "USE_COLORMATRIX", p->is_yuv);
+ shader_def_opt(&header_conv, "USE_LINEAR_CONV", convert_input_to_linear);
+
+ shader_def_opt(&header_final, "USE_LINEAR_CONV_INV", p->use_lut_3d);
+ shader_def_opt(&header_final, "USE_GAMMA_POW", p->use_gamma);
+ shader_def_opt(&header_final, "USE_3DLUT", p->use_lut_3d);
+ shader_def_opt(&header_final, "USE_SRGB", p->use_srgb);
+ shader_def_opt(&header_final, "USE_DITHER", p->dither_texture != 0);
+
+ if (p->use_scale_sep && p->scalers[0].kernel) {
+ header_sep = talloc_strdup(tmp, "");
+ shader_def_opt(&header_sep, "FIXED_SCALE", true);
+ shader_setup_scaler(&header_sep, &p->scalers[0], 0);
+ shader_setup_scaler(&header_final, &p->scalers[0], 1);
+ } else {
+ shader_setup_scaler(&header_final, &p->scalers[0], -1);
+ }
+
+ // We want to do scaling in linear light. Scaling is closely connected to
+ // texture sampling due to how the shader is structured (or if GL bilinear
+ // scaling is used). The purpose of the "indirect" pass is to convert the
+ // input video to linear RGB.
+ // Another purpose is reducing input to a single texture for scaling.
+ bool use_indirect = p->use_indirect;
+
+ // Don't sample from input video textures before converting the input to
+ // linear light. (Unneeded when sRGB textures are used.)
+ if (convert_input_to_linear)
+ use_indirect = true;
+
+ // It doesn't make sense to scale the chroma with cscale in the 1. scale
+ // step and with lscale in the 2. step. If the chroma is subsampled, a
+ // convolution filter wouldn't even work entirely correctly, because the
+ // luma scaler would sample two texels instead of one per tap for chroma.
+ // Also, even with 4:4:4 YUV or planar RGB, the indirection might be faster,
+ // because the shader can't use one scaler for sampling from 3 textures. It
+ // has to fetch the coefficients for each texture separately, even though
+ // they're the same (this is not an inherent restriction, but would require
+ // to restructure the shader).
+ if (header_sep && p->plane_count > 1)
+ use_indirect = true;
+
+ if (input_is_subsampled(p)) {
+ shader_setup_scaler(&header_conv, &p->scalers[1], -1);
+ } else {
+ // Force using the luma scaler on chroma. If the "indirect" stage is
+ // used, the actual scaling will happen in the next stage.
+ shader_def(&header_conv, "SAMPLE_C",
+ use_indirect ? "sample_bilinear" : "SAMPLE_L");
+ }
+
+ if (use_indirect) {
+ // We don't use filtering for the Y-plane (luma), because it's never
+ // scaled in this scenario.
+ shader_def(&header_conv, "SAMPLE_L", "sample_bilinear");
+ shader_def_opt(&header_conv, "FIXED_SCALE", true);
+ header_conv = t_concat(tmp, header, header_conv);
+ p->indirect_program =
+ create_program(gl, "indirect", header_conv, vertex_shader, s_video);
+ } else if (header_sep) {
+ header_sep = t_concat(tmp, header_sep, header_conv);
+ } else {
+ header_final = t_concat(tmp, header_final, header_conv);
+ }
+
+ if (header_sep) {
+ header_sep = t_concat(tmp, header, header_sep);
+ p->scale_sep_program =
+ create_program(gl, "scale_sep", header_sep, vertex_shader, s_video);
+ }
+
+ header_final = t_concat(tmp, header, header_final);
+ p->final_program =
+ create_program(gl, "final", header_final, vertex_shader, s_video);
+
+ debug_check_gl(p, "shader compilation");
+
+ talloc_free(tmp);
+}
+
+static void delete_program(GL *gl, GLuint *prog)
+{
+ gl->DeleteProgram(*prog);
+ *prog = 0;
+}
+
+static void delete_shaders(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+
+ for (int n = 0; n < SUBBITMAP_COUNT; n++)
+ delete_program(gl, &p->osd_programs[n]);
+ delete_program(gl, &p->indirect_program);
+ delete_program(gl, &p->scale_sep_program);
+ delete_program(gl, &p->final_program);
+}
+
+static double get_scale_factor(struct gl_priv *p)
+{
+ double sx = (p->dst_rect.x1 - p->dst_rect.x0) /
+ (double)(p->src_rect.x1 - p->src_rect.x0);
+ double sy = (p->dst_rect.y1 - p->dst_rect.y0) /
+ (double)(p->src_rect.y1 - p->src_rect.y0);
+ // xxx: actually we should use different scalers in X/Y directions if the
+ // scale factors are different due to anamorphic content
+ return FFMIN(sx, sy);
+}
+
+static bool update_scale_factor(struct gl_priv *p, struct filter_kernel *kernel)
+{
+ double scale = get_scale_factor(p);
+ if (!p->use_fancy_downscaling && scale < 1.0)
+ scale = 1.0;
+ return mp_init_filter(kernel, filter_sizes, FFMAX(1.0, 1.0 / scale));
+}
+
+static void init_scaler(struct gl_priv *p, struct scaler *scaler)
+{
+ GL *gl = p->gl;
+
+ assert(scaler->name);
+
+ scaler->kernel = NULL;
+
+ const struct filter_kernel *t_kernel = mp_find_filter_kernel(scaler->name);
+ if (!t_kernel)
+ return;
+
+ scaler->kernel_storage = *t_kernel;
+ scaler->kernel = &scaler->kernel_storage;
+
+ for (int n = 0; n < 2; n++) {
+ if (!isnan(p->scaler_params[n]))
+ scaler->kernel->params[n] = p->scaler_params[n];
+ }
+
+ update_scale_factor(p, scaler->kernel);
+
+ int size = scaler->kernel->size;
+ assert(size < FF_ARRAY_ELEMS(lut_tex_formats));
+ struct lut_tex_format *fmt = &lut_tex_formats[size];
+ bool use_2d = fmt->pixels > 1;
+ bool is_luma = scaler->index == 0;
+ scaler->lut_name = use_2d
+ ? (is_luma ? "lut_l_2d" : "lut_c_2d")
+ : (is_luma ? "lut_l_1d" : "lut_c_1d");
+
+ gl->ActiveTexture(GL_TEXTURE0 + TEXUNIT_SCALERS + scaler->index);
+ GLenum target = use_2d ? GL_TEXTURE_2D : GL_TEXTURE_1D;
+
+ if (!scaler->gl_lut)
+ gl->GenTextures(1, &scaler->gl_lut);
+
+ gl->BindTexture(target, scaler->gl_lut);
+ gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+
+ float *weights = talloc_array(NULL, float, LOOKUP_TEXTURE_SIZE * size);
+ mp_compute_lut(scaler->kernel, LOOKUP_TEXTURE_SIZE, weights);
+ if (use_2d) {
+ gl->TexImage2D(GL_TEXTURE_2D, 0, fmt->internal_format, fmt->pixels,
+ LOOKUP_TEXTURE_SIZE, 0, fmt->format, GL_FLOAT,
+ weights);
+ } else {
+ gl->TexImage1D(GL_TEXTURE_1D, 0, fmt->internal_format,
+ LOOKUP_TEXTURE_SIZE, 0, fmt->format, GL_FLOAT,
+ weights);
+ }
+ talloc_free(weights);
+
+ gl->TexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ gl->TexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ gl->TexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl->TexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ gl->ActiveTexture(GL_TEXTURE0);
+
+ debug_check_gl(p, "after initializing scaler");
+}
+
+static void make_dither_matrix(unsigned char *m, int size)
+{
+ m[0] = 0;
+ for (int sz = 1; sz < size; sz *= 2) {
+ int offset[] = {sz*size, sz, sz * (size+1), 0};
+ for (int i = 0; i < 4; i++)
+ for (int y = 0; y < sz * size; y += size)
+ for (int x = 0; x < sz; x++)
+ m[x+y+offset[i]] = m[x+y] * 4 + (3-i) * 256/size/size;
+ }
+}
+
+static void init_dither(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+
+ // Assume 8 bits per component if unknown.
+ int dst_depth = p->glctx->depth_g ? p->glctx->depth_g : 8;
+ if (p->dither_depth > 0)
+ dst_depth = p->dither_depth;
+
+ int src_depth = p->component_bits;
+ if (p->use_lut_3d)
+ src_depth = 16;
+
+ if (dst_depth >= src_depth || p->dither_depth < 0 || src_depth < 0)
+ return;
+
+ mp_msg(MSGT_VO, MSGL_V, "[gl] Dither %d->%d.\n", src_depth, dst_depth);
+
+ // This defines how many bits are considered significant for output on
+ // screen. The superfluous bits will be used for rounded according to the
+ // dither matrix. The precision of the source implicitly decides how many
+ // dither patterns can be visible.
+ p->dither_quantization = (1 << dst_depth) - 1;
+ int size = 8;
+ p->dither_multiply = p->dither_quantization + 1.0 / (size*size);
+ unsigned char dither[256];
+ make_dither_matrix(dither, size);
+
+ p->dither_size = size;
+
+ gl->ActiveTexture(GL_TEXTURE0 + TEXUNIT_DITHER);
+ gl->GenTextures(1, &p->dither_texture);
+ gl->BindTexture(GL_TEXTURE_2D, p->dither_texture);
+ gl->PixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RED, size, size, 0, GL_RED,
+ GL_UNSIGNED_BYTE, dither);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ gl->ActiveTexture(GL_TEXTURE0);
+}
+
+static void reinit_rendering(struct gl_priv *p)
+{
+ mp_msg(MSGT_VO, MSGL_V, "[gl] Reinit rendering.\n");
+
+ if (p->gl->SwapInterval && p->swap_interval >= 0)
+ p->gl->SwapInterval(p->swap_interval);
+
+ debug_check_gl(p, "before scaler initialization");
+
+ uninit_rendering(p);
+
+ init_dither(p);
+
+ init_scaler(p, &p->scalers[0]);
+ init_scaler(p, &p->scalers[1]);
+
+ compile_shaders(p);
+
+ if (p->indirect_program && !p->indirect_fbo.fbo)
+ fbotex_init(p, &p->indirect_fbo, p->texture_width, p->texture_height);
+
+ if (!p->osd) {
+ p->osd = mpgl_osd_init(p->gl, false);
+ p->osd->use_pbo = p->use_pbo;
+ }
+}
+
+static void uninit_rendering(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+
+ delete_shaders(p);
+
+ for (int n = 0; n < 2; n++) {
+ gl->DeleteTextures(1, &p->scalers[n].gl_lut);
+ p->scalers[n].gl_lut = 0;
+ p->scalers[n].lut_name = NULL;
+ p->scalers[n].kernel = NULL;
+ }
+
+ gl->DeleteTextures(1, &p->dither_texture);
+ p->dither_texture = 0;
+
+ if (p->osd)
+ mpgl_osd_destroy(p->osd);
+ p->osd = NULL;
+}
+
+static void init_lut_3d(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+
+ gl->GenTextures(1, &p->lut_3d_texture);
+ gl->ActiveTexture(GL_TEXTURE0 + TEXUNIT_3DLUT);
+ gl->BindTexture(GL_TEXTURE_3D, p->lut_3d_texture);
+ gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ gl->TexImage3D(GL_TEXTURE_3D, 0, GL_RGB16, p->lut_3d_w, p->lut_3d_h,
+ p->lut_3d_d, 0, GL_RGB, GL_UNSIGNED_SHORT, p->lut_3d_data);
+ gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
+ gl->ActiveTexture(GL_TEXTURE0);
+
+ debug_check_gl(p, "after 3d lut creation");
+}
+
+static void init_video(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+
+ if (p->use_lut_3d && !p->lut_3d_texture)
+ init_lut_3d(p);
+
+ if (!p->is_yuv && (p->use_srgb || p->use_lut_3d)) {
+ p->is_linear_rgb = true;
+ p->gl_internal_format = GL_SRGB;
+ }
+
+ int eq_caps = MP_CSP_EQ_CAPS_GAMMA;
+ if (p->is_yuv)
+ eq_caps |= MP_CSP_EQ_CAPS_COLORMATRIX;
+ p->video_eq.capabilities = eq_caps;
+
+ debug_check_gl(p, "before video texture creation");
+
+ tex_size(p, p->image_width, p->image_height,
+ &p->texture_width, &p->texture_height);
+
+ for (int n = 0; n < p->plane_count; n++) {
+ struct texplane *plane = &p->planes[n];
+
+ int w = p->texture_width >> plane->shift_x;
+ int h = p->texture_height >> plane->shift_y;
+
+ mp_msg(MSGT_VO, MSGL_V, "[gl] Texture for plane %d: %dx%d\n", n, w, h);
+
+ gl->ActiveTexture(GL_TEXTURE0 + n);
+ gl->GenTextures(1, &plane->gl_texture);
+ gl->BindTexture(GL_TEXTURE_2D, plane->gl_texture);
+
+ gl->TexImage2D(GL_TEXTURE_2D, 0, p->gl_internal_format, w, h, 0,
+ p->gl_format, p->gl_type, NULL);
+ default_tex_params(gl, GL_TEXTURE_2D, GL_LINEAR);
+ }
+ gl->ActiveTexture(GL_TEXTURE0);
+
+ debug_check_gl(p, "after video texture creation");
+
+ reinit_rendering(p);
+}
+
+static void uninit_video(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+
+ uninit_rendering(p);
+
+ for (int n = 0; n < 3; n++) {
+ struct texplane *plane = &p->planes[n];
+
+ gl->DeleteTextures(1, &plane->gl_texture);
+ plane->gl_texture = 0;
+ gl->DeleteBuffers(1, &plane->gl_buffer);
+ plane->gl_buffer = 0;
+ plane->buffer_ptr = NULL;
+ plane->buffer_size = 0;
+ }
+
+ fbotex_uninit(p, &p->indirect_fbo);
+ fbotex_uninit(p, &p->scale_sep_fbo);
+}
+
+static void render_to_fbo(struct gl_priv *p, struct fbotex *fbo, int w, int h,
+ int tex_w, int tex_h)
+{
+ GL *gl = p->gl;
+
+ gl->Viewport(0, 0, fbo->vp_w, fbo->vp_h);
+ gl->BindFramebuffer(GL_FRAMEBUFFER, fbo->fbo);
+
+ struct vertex vb[VERTICES_PER_QUAD];
+ write_quad(vb, -1, -1, 1, 1,
+ 0, 0, w, h,
+ tex_w, tex_h,
+ NULL, false);
+ draw_triangles(p, vb, VERTICES_PER_QUAD);
+
+ gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
+ gl->Viewport(p->vp_x, p->vp_y, p->vp_w, p->vp_h);
+
+}
+
+static void handle_pass(struct gl_priv *p, struct fbotex **source,
+ struct fbotex *fbo, GLuint program)
+{
+ GL *gl = p->gl;
+
+ if (!program)
+ return;
+
+ gl->BindTexture(GL_TEXTURE_2D, (*source)->texture);
+ gl->UseProgram(program);
+ render_to_fbo(p, fbo, (*source)->vp_w, (*source)->vp_h,
+ (*source)->tex_w, (*source)->tex_h);
+ *source = fbo;
+}
+
+static void do_render(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+ struct vertex vb[VERTICES_PER_QUAD];
+ bool is_flipped = p->mpi_flipped ^ p->vo_flipped;
+
+ // Order of processing:
+ // [indirect -> [scale_sep ->]] final
+
+ struct fbotex dummy = {
+ .vp_w = p->image_width, .vp_h = p->image_height,
+ .tex_w = p->texture_width, .tex_h = p->texture_height,
+ .texture = p->planes[0].gl_texture,
+ };
+ struct fbotex *source = &dummy;
+
+ handle_pass(p, &source, &p->indirect_fbo, p->indirect_program);
+ handle_pass(p, &source, &p->scale_sep_fbo, p->scale_sep_program);
+
+ gl->BindTexture(GL_TEXTURE_2D, source->texture);
+ gl->UseProgram(p->final_program);
+
+ float final_texw = p->image_width * source->tex_w / (float)source->vp_w;
+ float final_texh = p->image_height * source->tex_h / (float)source->vp_h;
+
+ if (p->stereo_mode) {
+ int w = p->src_rect.x1 - p->src_rect.x0;
+ int imgw = p->image_width;
+
+ glEnable3DLeft(gl, p->stereo_mode);
+
+ write_quad(vb,
+ p->dst_rect.x0, p->dst_rect.y0,
+ p->dst_rect.x1, p->dst_rect.y1,
+ p->src_rect.x0 / 2, p->src_rect.y0,
+ p->src_rect.x0 / 2 + w / 2, p->src_rect.y1,
+ final_texw, final_texh,
+ NULL, is_flipped);
+ draw_triangles(p, vb, VERTICES_PER_QUAD);
+
+ glEnable3DRight(gl, p->stereo_mode);
+
+ write_quad(vb,
+ p->dst_rect.x0, p->dst_rect.y0,
+ p->dst_rect.x1, p->dst_rect.y1,
+ p->src_rect.x0 / 2 + imgw / 2, p->src_rect.y0,
+ p->src_rect.x0 / 2 + imgw / 2 + w / 2, p->src_rect.y1,
+ final_texw, final_texh,
+ NULL, is_flipped);
+ draw_triangles(p, vb, VERTICES_PER_QUAD);
+
+ glDisable3D(gl, p->stereo_mode);
+ } else {
+ write_quad(vb,
+ p->dst_rect.x0, p->dst_rect.y0,
+ p->dst_rect.x1, p->dst_rect.y1,
+ p->src_rect.x0, p->src_rect.y0,
+ p->src_rect.x1, p->src_rect.y1,
+ final_texw, final_texh,
+ NULL, is_flipped);
+ draw_triangles(p, vb, VERTICES_PER_QUAD);
+ }
+
+ gl->UseProgram(0);
+
+ debug_check_gl(p, "after video rendering");
+}
+
+static void update_window_sized_objects(struct gl_priv *p)
+{
+ if (p->scale_sep_program) {
+ int h = p->dst_rect.y1 - p->dst_rect.y0;
+ if (h > p->scale_sep_fbo.tex_h) {
+ fbotex_uninit(p, &p->scale_sep_fbo);
+ // Round up to an arbitrary alignment to make window resizing or
+ // panscan controls smoother (less texture reallocations).
+ int height = FFALIGN(h, 256);
+ fbotex_init(p, &p->scale_sep_fbo, p->image_width, height);
+ }
+ p->scale_sep_fbo.vp_w = p->image_width;
+ p->scale_sep_fbo.vp_h = h;
+ }
+}
+
+static void resize(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+ struct vo *vo = p->vo;
+
+ mp_msg(MSGT_VO, MSGL_V, "[gl] Resize: %dx%d\n", vo->dwidth, vo->dheight);
+ p->vp_x = 0, p->vp_y = 0;
+ p->vp_w = vo->dwidth, p->vp_h = vo->dheight;
+ gl->Viewport(p->vp_x, p->vp_y, p->vp_w, p->vp_h);
+
+ vo_get_src_dst_rects(vo, &p->src_rect, &p->dst_rect, &p->osd_rect);
+
+ bool need_scaler_reinit = false; // filter size change needed
+ bool need_scaler_update = false; // filter LUT change needed
+ bool too_small = false;
+ for (int n = 0; n < 2; n++) {
+ if (p->scalers[n].kernel) {
+ struct filter_kernel tkernel = *p->scalers[n].kernel;
+ struct filter_kernel old = tkernel;
+ bool ok = update_scale_factor(p, &tkernel);
+ too_small |= !ok;
+ need_scaler_reinit |= (tkernel.size != old.size);
+ need_scaler_update |= (tkernel.inv_scale != old.inv_scale);
+ }
+ }
+ if (need_scaler_reinit) {
+ reinit_rendering(p);
+ } else if (need_scaler_update) {
+ init_scaler(p, &p->scalers[0]);
+ init_scaler(p, &p->scalers[1]);
+ }
+ if (too_small)
+ mp_msg(MSGT_VO, MSGL_WARN, "[gl] Can't downscale that much, window "
+ "output may look suboptimal.\n");
+
+ update_window_sized_objects(p);
+ update_all_uniforms(p);
+
+ gl->Clear(GL_COLOR_BUFFER_BIT);
+ vo->want_redraw = true;
+}
+
+static void flip_page(struct vo *vo)
+{
+ struct gl_priv *p = vo->priv;
+ GL *gl = p->gl;
+
+ if (p->use_glFinish)
+ gl->Finish();
+
+ p->glctx->swapGlBuffers(p->glctx);
+
+ if (p->dst_rect.x0 > p->vp_x || p->dst_rect.y0 > p->vp_y
+ || p->dst_rect.x1 < p->vp_x + p->vp_w
+ || p->dst_rect.y1 < p->vp_y + p->vp_h)
+ {
+ gl->Clear(GL_COLOR_BUFFER_BIT);
+ }
+
+ p->frames_rendered++;
+}
+
+static int draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h,
+ int x, int y)
+{
+ struct gl_priv *p = vo->priv;
+ GL *gl = p->gl;
+
+ p->mpi_flipped = stride[0] < 0;
+
+ for (int n = 0; n < p->plane_count; n++) {
+ gl->ActiveTexture(GL_TEXTURE0 + n);
+ gl->BindTexture(GL_TEXTURE_2D, p->planes[n].gl_texture);
+ int xs = p->planes[n].shift_x, ys = p->planes[n].shift_y;
+ glUploadTex(gl, GL_TEXTURE_2D, p->gl_format, p->gl_type, src[n],
+ stride[n], x >> xs, y >> ys, w >> xs, h >> ys, 0);
+ }
+ gl->ActiveTexture(GL_TEXTURE0);
+
+ return 0;
+}
+
+static uint32_t get_image(struct vo *vo, mp_image_t *mpi)
+{
+ struct gl_priv *p = vo->priv;
+ GL *gl = p->gl;
+
+ if (!p->use_pbo)
+ return VO_FALSE;
+
+ // We don't support alpha planes. (Disabling PBOs with normal draw calls is
+ // an undesired, but harmless side-effect.)
+ if (mpi->num_planes != p->plane_count)
+ return VO_FALSE;
+
+ if (mpi->flags & MP_IMGFLAG_READABLE)
+ return VO_FALSE;
+ if (mpi->type != MP_IMGTYPE_STATIC && mpi->type != MP_IMGTYPE_TEMP &&
+ (mpi->type != MP_IMGTYPE_NUMBERED || mpi->number))
+ return VO_FALSE;
+ mpi->flags &= ~MP_IMGFLAG_COMMON_PLANE;
+ for (int n = 0; n < p->plane_count; n++) {
+ struct texplane *plane = &p->planes[n];
+ mpi->stride[n] = (mpi->width >> plane->shift_x) * p->plane_bytes;
+ int needed_size = (mpi->height >> plane->shift_y) * mpi->stride[n];
+ if (!plane->gl_buffer)
+ gl->GenBuffers(1, &plane->gl_buffer);
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, plane->gl_buffer);
+ if (needed_size > plane->buffer_size) {
+ plane->buffer_size = needed_size;
+ gl->BufferData(GL_PIXEL_UNPACK_BUFFER, plane->buffer_size,
+ NULL, GL_DYNAMIC_DRAW);
+ }
+ if (!plane->buffer_ptr)
+ plane->buffer_ptr = gl->MapBuffer(GL_PIXEL_UNPACK_BUFFER,
+ GL_WRITE_ONLY);
+ mpi->planes[n] = plane->buffer_ptr;
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+ }
+ mpi->flags |= MP_IMGFLAG_DIRECT;
+ return VO_TRUE;
+}
+
+static uint32_t draw_image(struct gl_priv *p, mp_image_t *mpi)
+{
+ GL *gl = p->gl;
+ int n;
+
+ assert(mpi->num_planes >= p->plane_count);
+
+ mp_image_t mpi2 = *mpi;
+ int w = mpi->w, h = mpi->h;
+ if (mpi->flags & MP_IMGFLAG_DRAW_CALLBACK)
+ goto skip_upload;
+ mpi2.flags = 0;
+ mpi2.type = MP_IMGTYPE_TEMP;
+ mpi2.width = mpi2.w;
+ mpi2.height = mpi2.h;
+ if (!(mpi->flags & MP_IMGFLAG_DIRECT)
+ && !p->planes[0].buffer_ptr
+ && get_image(p->vo, &mpi2) == VO_TRUE)
+ {
+ for (n = 0; n < p->plane_count; n++) {
+ struct texplane *plane = &p->planes[n];
+ int xs = plane->shift_x, ys = plane->shift_y;
+ int line_bytes = (mpi->w >> xs) * p->plane_bytes;
+ memcpy_pic(mpi2.planes[n], mpi->planes[n], line_bytes, mpi->h >> ys,
+ mpi2.stride[n], mpi->stride[n]);
+ }
+ mpi = &mpi2;
+ }
+ p->mpi_flipped = mpi->stride[0] < 0;
+ for (n = 0; n < p->plane_count; n++) {
+ struct texplane *plane = &p->planes[n];
+ int xs = plane->shift_x, ys = plane->shift_y;
+ void *plane_ptr = mpi->planes[n];
+ if (mpi->flags & MP_IMGFLAG_DIRECT) {
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, plane->gl_buffer);
+ if (!gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER))
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Video PBO upload failed. "
+ "Remove the 'pbo' suboption.\n");
+ plane->buffer_ptr = NULL;
+ plane_ptr = NULL; // PBO offset 0
+ }
+ gl->ActiveTexture(GL_TEXTURE0 + n);
+ gl->BindTexture(GL_TEXTURE_2D, plane->gl_texture);
+ glUploadTex(gl, GL_TEXTURE_2D, p->gl_format, p->gl_type, plane_ptr,
+ mpi->stride[n], 0, 0, w >> xs, h >> ys, 0);
+ }
+ gl->ActiveTexture(GL_TEXTURE0);
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+skip_upload:
+ do_render(p);
+ return VO_TRUE;
+}
+
+static mp_image_t *get_screenshot(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+
+ mp_image_t *image = alloc_mpi(p->texture_width, p->texture_height,
+ p->image_format);
+
+ // NOTE about image formats with alpha plane: we don't even have the alpha
+ // anymore. We never upload it to any texture, as it would be a waste of
+ // time. On the other hand, we can't find a "similar", non-alpha image
+ // format easily. So we just leave the alpha plane of the newly allocated
+ // image as-is, and hope that the alpha is ignored by the receiver of the
+ // screenshot. (If not, code should be added to make it fully opaque.)
+
+ for (int n = 0; n < p->plane_count; n++) {
+ gl->ActiveTexture(GL_TEXTURE0 + n);
+ gl->BindTexture(GL_TEXTURE_2D, p->planes[n].gl_texture);
+ glDownloadTex(gl, GL_TEXTURE_2D, p->gl_format, p->gl_type,
+ image->planes[n], image->stride[n]);
+ }
+ gl->ActiveTexture(GL_TEXTURE0);
+
+ image->w = p->image_width;
+ image->h = p->image_height;
+ image->display_w = p->vo->aspdat.prew;
+ image->display_h = p->vo->aspdat.preh;
+
+ mp_image_set_colorspace_details(image, &p->colorspace);
+
+ return image;
+}
+
+static void draw_osd_cb(void *ctx, struct sub_bitmaps *imgs)
+{
+ struct gl_priv *p = ctx;
+ GL *gl = p->gl;
+
+ struct mpgl_osd_part *osd = mpgl_osd_generate(p->osd, imgs);
+ if (!osd)
+ return;
+
+ assert(osd->format != SUBBITMAP_EMPTY);
+
+ if (!osd->num_vertices) {
+ osd->vertices = talloc_realloc(osd, osd->vertices, struct vertex,
+ osd->packer->count * VERTICES_PER_QUAD);
+
+ struct vertex *va = osd->vertices;
+
+ for (int n = 0; n < osd->packer->count; n++) {
+ struct sub_bitmap *b = &imgs->parts[n];
+ struct pos p = osd->packer->result[n];
+
+ // NOTE: the blend color is used with SUBBITMAP_LIBASS only, so it
+ // doesn't matter that we upload garbage for the other formats
+ uint32_t c = b->libass.color;
+ uint8_t color[4] = { c >> 24, (c >> 16) & 0xff,
+ (c >> 8) & 0xff, 255 - (c & 0xff) };
+
+ write_quad(&va[osd->num_vertices],
+ b->x, b->y, b->x + b->dw, b->y + b->dh,
+ p.x, p.y, p.x + b->w, p.y + b->h,
+ osd->w, osd->h, color, false);
+ osd->num_vertices += VERTICES_PER_QUAD;
+ }
+ }
+
+ debug_check_gl(p, "before drawing osd");
+
+ gl->UseProgram(p->osd_programs[osd->format]);
+ mpgl_osd_set_gl_state(p->osd, osd);
+ draw_triangles(p, osd->vertices, osd->num_vertices);
+ mpgl_osd_unset_gl_state(p->osd, osd);
+ gl->UseProgram(0);
+
+ debug_check_gl(p, "after drawing osd");
+}
+
+static void draw_osd(struct vo *vo, struct osd_state *osd)
+{
+ struct gl_priv *p = vo->priv;
+ assert(p->osd);
+
+ osd_draw(osd, p->osd_rect, osd->vo_pts, 0, p->osd->formats, draw_osd_cb, p);
+}
+
+// Disable features that are not supported with the current OpenGL version.
+static void check_gl_features(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+ bool have_float_tex = gl->mpgl_caps & MPGL_CAP_FLOAT_TEX;
+ bool have_fbo = gl->mpgl_caps & MPGL_CAP_FB;
+ bool have_srgb = gl->mpgl_caps & MPGL_CAP_SRGB_TEX;
+
+ // srgb_compand() not available
+ if (gl->glsl_version < 130)
+ have_srgb = false;
+
+ char *disabled[10];
+ int n_disabled = 0;
+
+ if (have_fbo) {
+ struct fbotex fbo = {0};
+ have_fbo = fbotex_init(p, &fbo, 16, 16);
+ fbotex_uninit(p, &fbo);
+ }
+
+ // Disable these only if the user didn't disable scale-sep on the command
+ // line, so convolution filter can still be forced to be run.
+ // Normally, we want to disable them by default if FBOs are unavailable,
+ // because they will be slow (not critically slow, but still slower).
+ // Without FP textures, we must always disable them.
+ if (!have_float_tex || (!have_fbo && p->use_scale_sep)) {
+ for (int n = 0; n < 2; n++) {
+ struct scaler *scaler = &p->scalers[n];
+ if (mp_find_filter_kernel(scaler->name)) {
+ scaler->name = "bilinear";
+ disabled[n_disabled++]
+ = have_float_tex ? "scaler (FBO)" : "scaler (float tex.)";
+ }
+ }
+ }
+
+ if (!have_srgb && p->use_srgb) {
+ p->use_srgb = false;
+ disabled[n_disabled++] = "sRGB";
+ }
+ if (!have_fbo && p->use_lut_3d) {
+ p->use_lut_3d = false;
+ disabled[n_disabled++] = "color management (FBO)";
+ }
+ if (!have_srgb && p->use_lut_3d) {
+ p->use_lut_3d = false;
+ disabled[n_disabled++] = "color management (sRGB)";
+ }
+
+ if (!have_fbo) {
+ p->use_scale_sep = false;
+ p->use_indirect = false;
+ }
+
+ if (n_disabled) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] Some OpenGL extensions not detected, "
+ "disabling: ");
+ for (int n = 0; n < n_disabled; n++) {
+ if (n)
+ mp_msg(MSGT_VO, MSGL_ERR, ", ");
+ mp_msg(MSGT_VO, MSGL_ERR, "%s", disabled[n]);
+ }
+ mp_msg(MSGT_VO, MSGL_ERR, ".\n");
+ }
+}
+
+static void setup_vertex_array(GL *gl)
+{
+ size_t stride = sizeof(struct vertex);
+
+ gl->EnableVertexAttribArray(VERTEX_ATTRIB_POSITION);
+ gl->VertexAttribPointer(VERTEX_ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE,
+ stride, (void*)offsetof(struct vertex, position));
+
+ gl->EnableVertexAttribArray(VERTEX_ATTRIB_COLOR);
+ gl->VertexAttribPointer(VERTEX_ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE,
+ stride, (void*)offsetof(struct vertex, color));
+
+ gl->EnableVertexAttribArray(VERTEX_ATTRIB_TEXCOORD);
+ gl->VertexAttribPointer(VERTEX_ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE,
+ stride, (void*)offsetof(struct vertex, texcoord));
+}
+
+static int init_gl(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+
+ debug_check_gl(p, "before init_gl");
+
+ const char *vendor = gl->GetString(GL_VENDOR);
+ const char *version = gl->GetString(GL_VERSION);
+ const char *renderer = gl->GetString(GL_RENDERER);
+ const char *glsl = gl->GetString(GL_SHADING_LANGUAGE_VERSION);
+ mp_msg(MSGT_VO, MSGL_V, "[gl] GL_RENDERER='%s', GL_VENDOR='%s', "
+ "GL_VERSION='%s', GL_SHADING_LANGUAGE_VERSION='%s'"
+ "\n", renderer, vendor, version, glsl);
+ mp_msg(MSGT_VO, MSGL_V, "[gl] Display depth: R=%d, G=%d, B=%d\n",
+ p->glctx->depth_r, p->glctx->depth_g, p->glctx->depth_b);
+
+ check_gl_features(p);
+
+ gl->Disable(GL_DITHER);
+ gl->Disable(GL_BLEND);
+ gl->Disable(GL_DEPTH_TEST);
+ gl->DepthMask(GL_FALSE);
+ gl->Disable(GL_CULL_FACE);
+ gl->DrawBuffer(GL_BACK);
+
+ gl->GenBuffers(1, &p->vertex_buffer);
+ gl->BindBuffer(GL_ARRAY_BUFFER, p->vertex_buffer);
+
+ if (gl->BindVertexArray) {
+ gl->GenVertexArrays(1, &p->vao);
+ gl->BindVertexArray(p->vao);
+ setup_vertex_array(gl);
+ gl->BindVertexArray(0);
+ } else {
+ setup_vertex_array(gl);
+ }
+
+ gl->BindBuffer(GL_ARRAY_BUFFER, 0);
+
+ gl->ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ gl->Clear(GL_COLOR_BUFFER_BIT);
+
+ debug_check_gl(p, "after init_gl");
+
+ return 1;
+}
+
+static void uninit_gl(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+
+ // NOTE: GL functions might not be loaded yet
+ if (!(p->glctx && p->gl->DeleteTextures))
+ return;
+
+ uninit_video(p);
+
+ if (gl->DeleteVertexArrays)
+ gl->DeleteVertexArrays(1, &p->vao);
+ p->vao = 0;
+ gl->DeleteBuffers(1, &p->vertex_buffer);
+ p->vertex_buffer = 0;
+
+ gl->DeleteTextures(1, &p->lut_3d_texture);
+ p->lut_3d_texture = 0;
+}
+
+static bool init_format(int fmt, struct gl_priv *init)
+{
+ bool supported = false;
+ struct gl_priv dummy;
+ if (!init)
+ init = &dummy;
+
+ mp_image_t dummy_img = {0};
+ mp_image_setfmt(&dummy_img, fmt);
+
+ init->image_format = fmt;
+ init->component_bits = -1;
+
+ // RGB/packed formats
+ for (const struct fmt_entry *e = mp_to_gl_formats; e->mp_format; e++) {
+ if (e->mp_format == fmt) {
+ supported = true;
+ init->plane_bits = dummy_img.bpp;
+ init->gl_format = e->format;
+ init->gl_internal_format = e->internal_format;
+ init->component_bits = e->component_bits;
+ init->gl_type = e->type;
+ break;
+ }
+ }
+
+ // YUV/planar formats
+ if (!supported && mp_get_chroma_shift(fmt, NULL, NULL, &init->plane_bits)) {
+ init->gl_format = GL_RED;
+ init->component_bits = init->plane_bits;
+ if (init->plane_bits == 8) {
+ supported = true;
+ init->gl_internal_format = GL_RED;
+ init->gl_type = GL_UNSIGNED_BYTE;
+ } else if (IMGFMT_IS_YUVP16_NE(fmt)) {
+ supported = true;
+ init->gl_internal_format = GL_R16;
+ init->gl_type = GL_UNSIGNED_SHORT;
+ }
+ }
+
+ // RGB/planar
+ if (!supported && fmt == IMGFMT_GBRP) {
+ supported = true;
+ init->plane_bits = init->component_bits = 8;
+ init->gl_format = GL_RED;
+ init->gl_internal_format = GL_RED;
+ init->gl_type = GL_UNSIGNED_BYTE;
+ }
+
+ if (!supported)
+ return false;
+
+ init->plane_bytes = (init->plane_bits + 7) / 8;
+ init->is_yuv = dummy_img.flags & MP_IMGFLAG_YUV;
+ init->is_linear_rgb = false;
+
+ // NOTE: we throw away the additional alpha plane, if one exists.
+ init->plane_count = dummy_img.num_planes > 2 ? 3 : 1;
+ assert(dummy_img.num_planes >= init->plane_count);
+ assert(dummy_img.num_planes <= init->plane_count + 1);
+
+ for (int n = 0; n < init->plane_count; n++) {
+ struct texplane *plane = &init->planes[n];
+
+ plane->shift_x = n > 0 ? dummy_img.chroma_x_shift : 0;
+ plane->shift_y = n > 0 ? dummy_img.chroma_y_shift : 0;
+ }
+
+ return true;
+}
+
+static int query_format(uint32_t format)
+{
+ int caps = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_FLIP |
+ VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | VFCAP_ACCEPT_STRIDE |
+ VFCAP_OSD;
+ if (!init_format(format, NULL))
+ return 0;
+ return caps;
+}
+
+static bool create_window(struct gl_priv *p, uint32_t d_width,
+ uint32_t d_height, uint32_t flags)
+{
+ if (p->stereo_mode == GL_3D_QUADBUFFER)
+ flags |= VOFLAG_STEREO;
+
+ if (p->use_gl_debug)
+ flags |= VOFLAG_GL_DEBUG;
+
+ int mpgl_caps = MPGL_CAP_GL21 | MPGL_CAP_TEX_RG;
+ if (!p->allow_sw)
+ mpgl_caps |= MPGL_CAP_NO_SW;
+ return mpgl_create_window(p->glctx, mpgl_caps, d_width, d_height, flags);
+}
+
+static int config(struct vo *vo, uint32_t width, uint32_t height,
+ uint32_t d_width, uint32_t d_height, uint32_t flags,
+ uint32_t format)
+{
+ struct gl_priv *p = vo->priv;
+
+ if (!create_window(p, d_width, d_height, flags))
+ return -1;
+
+ if (!p->vertex_buffer)
+ init_gl(p);
+
+ p->vo_flipped = !!(flags & VOFLAG_FLIPPING);
+
+ if (p->image_format != format || p->image_width != width
+ || p->image_height != height)
+ {
+ uninit_video(p);
+ p->image_height = height;
+ p->image_width = width;
+ init_format(format, p);
+ init_video(p);
+ }
+
+ resize(p);
+
+ return 0;
+}
+
+static void check_events(struct vo *vo)
+{
+ struct gl_priv *p = vo->priv;
+
+ int e = p->glctx->check_events(vo);
+ if (e & VO_EVENT_REINIT) {
+ uninit_gl(p);
+ init_gl(p);
+ init_video(p);
+ resize(p);
+ }
+ if (e & VO_EVENT_RESIZE)
+ resize(p);
+ if (e & VO_EVENT_EXPOSE)
+ vo->want_redraw = true;
+}
+
+static int control(struct vo *vo, uint32_t request, void *data)
+{
+ struct gl_priv *p = vo->priv;
+
+ switch (request) {
+ case VOCTRL_QUERY_FORMAT:
+ return query_format(*(uint32_t *)data);
+ case VOCTRL_DRAW_IMAGE:
+ return draw_image(p, data);
+ case VOCTRL_ONTOP:
+ if (!p->glctx->ontop)
+ break;
+ p->glctx->ontop(vo);
+ return VO_TRUE;
+ case VOCTRL_PAUSE:
+ if (!p->glctx->pause)
+ break;
+ p->glctx->pause(vo);
+ return VO_TRUE;
+ case VOCTRL_RESUME:
+ if (!p->glctx->resume)
+ break;
+ p->glctx->resume(vo);
+ return VO_TRUE;
+ case VOCTRL_FULLSCREEN:
+ p->glctx->fullscreen(vo);
+ resize(p);
+ return VO_TRUE;
+ case VOCTRL_BORDER:
+ if (!p->glctx->border)
+ break;
+ p->glctx->border(vo);
+ resize(p);
+ return VO_TRUE;
+ case VOCTRL_GET_PANSCAN:
+ return VO_TRUE;
+ case VOCTRL_SET_PANSCAN:
+ resize(p);
+ return VO_TRUE;
+ case VOCTRL_GET_EQUALIZER: {
+ struct voctrl_get_equalizer_args *args = data;
+ return mp_csp_equalizer_get(&p->video_eq, args->name, args->valueptr)
+ >= 0 ? VO_TRUE : VO_NOTIMPL;
+ }
+ case VOCTRL_SET_EQUALIZER: {
+ struct voctrl_set_equalizer_args *args = data;
+ if (mp_csp_equalizer_set(&p->video_eq, args->name, args->value) < 0)
+ return VO_NOTIMPL;
+ if (!p->use_gamma && p->video_eq.values[MP_CSP_EQ_GAMMA] != 0) {
+ mp_msg(MSGT_VO, MSGL_V, "[gl] Auto-enabling gamma.\n");
+ p->use_gamma = true;
+ compile_shaders(p);
+ }
+ update_all_uniforms(p);
+ vo->want_redraw = true;
+ return VO_TRUE;
+ }
+ case VOCTRL_SET_YUV_COLORSPACE: {
+ if (p->is_yuv) {
+ p->colorspace = *(struct mp_csp_details *)data;
+ update_all_uniforms(p);
+ vo->want_redraw = true;
+ }
+ return VO_TRUE;
+ }
+ case VOCTRL_GET_YUV_COLORSPACE:
+ *(struct mp_csp_details *)data = p->colorspace;
+ return VO_TRUE;
+ case VOCTRL_UPDATE_SCREENINFO:
+ if (!p->glctx->update_xinerama_info)
+ break;
+ p->glctx->update_xinerama_info(vo);
+ return VO_TRUE;
+ case VOCTRL_SCREENSHOT: {
+ struct voctrl_screenshot_args *args = data;
+ if (args->full_window)
+ args->out_image = glGetWindowScreenshot(p->gl);
+ else
+ args->out_image = get_screenshot(p);
+ return true;
+ }
+ case VOCTRL_REDRAW_FRAME:
+ do_render(p);
+ return true;
+ case VOCTRL_SET_COMMAND_LINE: {
+ char *arg = data;
+ if (!reparse_cmdline(p, arg))
+ return false;
+ check_gl_features(p);
+ reinit_rendering(p);
+ resize(p);
+ vo->want_redraw = true;
+ return true;
+ }
+ }
+ return VO_NOTIMPL;
+}
+
+static void uninit(struct vo *vo)
+{
+ struct gl_priv *p = vo->priv;
+
+ uninit_gl(p);
+ mpgl_uninit(p->glctx);
+ p->glctx = NULL;
+ p->gl = NULL;
+}
+
+#ifdef CONFIG_LCMS2
+
+static void lcms2_error_handler(cmsContext ctx, cmsUInt32Number code,
+ const char *msg)
+{
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] lcms2: %s\n", msg);
+}
+
+static struct bstr load_file(struct gl_priv *p, void *talloc_ctx,
+ const char *filename)
+{
+ struct bstr res = {0};
+ stream_t *s = open_stream(filename, p->vo->opts, NULL);
+ if (s) {
+ res = stream_read_complete(s, talloc_ctx, 1000000000, 0);
+ free_stream(s);
+ }
+ return res;
+}
+
+#define LUT3D_CACHE_HEADER "mpv 3dlut cache 1.0\n"
+
+static bool load_icc(struct gl_priv *p, const char *icc_file,
+ const char *icc_cache, int icc_intent,
+ int s_r, int s_g, int s_b)
+{
+ void *tmp = talloc_new(p);
+ uint16_t *output = talloc_array(tmp, uint16_t, s_r * s_g * s_b * 3);
+
+ if (icc_intent == -1)
+ icc_intent = INTENT_ABSOLUTE_COLORIMETRIC;
+
+ mp_msg(MSGT_VO, MSGL_INFO, "[gl] Opening ICC profile '%s'\n", icc_file);
+ struct bstr iccdata = load_file(p, tmp, icc_file);
+ if (!iccdata.len)
+ goto error_exit;
+
+ char *cache_info = talloc_asprintf(tmp, "intent=%d, size=%dx%dx%d\n",
+ icc_intent, s_r, s_g, s_b);
+
+ // check cache
+ if (icc_cache) {
+ mp_msg(MSGT_VO, MSGL_INFO, "[gl] Opening 3D LUT cache in file '%s'.\n",
+ icc_cache);
+ struct bstr cachedata = load_file(p, tmp, icc_cache);
+ if (bstr_eatstart(&cachedata, bstr0(LUT3D_CACHE_HEADER))
+ && bstr_eatstart(&cachedata, bstr0(cache_info))
+ && bstr_eatstart(&cachedata, iccdata)
+ && cachedata.len == talloc_get_size(output))
+ {
+ memcpy(output, cachedata.start, cachedata.len);
+ goto done;
+ } else {
+ mp_msg(MSGT_VO, MSGL_WARN, "[gl] 3D LUT cache invalid!\n");
+ }
+ }
+
+ cmsSetLogErrorHandler(lcms2_error_handler);
+
+ cmsHPROFILE profile = cmsOpenProfileFromMem(iccdata.start, iccdata.len);
+ if (!profile)
+ goto error_exit;
+
+ cmsCIExyY d65;
+ cmsWhitePointFromTemp(&d65, 6504);
+ static const cmsCIExyYTRIPLE bt709prim = {
+ .Red = {0.64, 0.33, 1.0},
+ .Green = {0.30, 0.60, 1.0},
+ .Blue = {0.15, 0.06, 1.0},
+ };
+ cmsToneCurve *tonecurve = cmsBuildGamma(NULL, 2.2);
+ cmsHPROFILE vid_profile = cmsCreateRGBProfile(&d65, &bt709prim,
+ (cmsToneCurve*[3]){tonecurve, tonecurve, tonecurve});
+ cmsFreeToneCurve(tonecurve);
+ cmsHTRANSFORM trafo = cmsCreateTransform(vid_profile, TYPE_RGB_16,
+ profile, TYPE_RGB_16,
+ icc_intent,
+ cmsFLAGS_HIGHRESPRECALC);
+ cmsCloseProfile(profile);
+ cmsCloseProfile(vid_profile);
+
+ if (!trafo)
+ goto error_exit;
+
+ // transform a (s_r)x(s_g)x(s_b) cube, with 3 components per channel
+ uint16_t *input = talloc_array(tmp, uint16_t, s_r * 3);
+ for (int b = 0; b < s_b; b++) {
+ for (int g = 0; g < s_g; g++) {
+ for (int r = 0; r < s_r; r++) {
+ input[r * 3 + 0] = r * 65535 / (s_r - 1);
+ input[r * 3 + 1] = g * 65535 / (s_g - 1);
+ input[r * 3 + 2] = b * 65535 / (s_b - 1);
+ }
+ size_t base = (b * s_r * s_g + g * s_r) * 3;
+ cmsDoTransform(trafo, input, output + base, s_r);
+ }
+ }
+
+ cmsDeleteTransform(trafo);
+
+ if (icc_cache) {
+ FILE *out = fopen(icc_cache, "wb");
+ if (out) {
+ fprintf(out, "%s%s", LUT3D_CACHE_HEADER, cache_info);
+ fwrite(iccdata.start, iccdata.len, 1, out);
+ fwrite(output, talloc_get_size(output), 1, out);
+ fclose(out);
+ }
+ }
+
+done:
+
+ p->lut_3d_data = talloc_steal(p, output);
+ p->lut_3d_w = s_r, p->lut_3d_h = s_g, p->lut_3d_d = s_b;
+ p->use_lut_3d = true;
+
+ talloc_free(tmp);
+ return true;
+
+error_exit:
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Error loading ICC profile.\n");
+ talloc_free(tmp);
+ return false;
+}
+
+#else /* CONFIG_LCMS2 */
+
+static bool load_icc(struct gl_priv *p, ...)
+{
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] LCMS2 support not compiled.\n");
+ return false;
+}
+
+#endif /* CONFIG_LCMS2 */
+
+static bool parse_3dlut_size(const char *s, int *p1, int *p2, int *p3)
+{
+ if (sscanf(s, "%dx%dx%d", p1, p2, p3) != 3)
+ return false;
+ for (int n = 0; n < 3; n++) {
+ int s = ((int[]) { *p1, *p2, *p3 })[n];
+ if (s < 2 || s > 256 || ((s - 1) & s))
+ return false;
+ }
+ return true;
+}
+
+static int lut3d_size_valid(void *arg)
+{
+ char *s = *(char **)arg;
+ int p1, p2, p3;
+ return parse_3dlut_size(s, &p1, &p2, &p3);
+}
+
+static int backend_valid(void *arg)
+{
+ return mpgl_find_backend(*(const char **)arg) >= 0;
+}
+
+struct fbo_format {
+ const char *name;
+ GLint format;
+};
+
+const struct fbo_format fbo_formats[] = {
+ {"rgb", GL_RGB},
+ {"rgba", GL_RGBA},
+ {"rgb8", GL_RGB8},
+ {"rgb10", GL_RGB10},
+ {"rgb16", GL_RGB16},
+ {"rgb16f", GL_RGB16F},
+ {"rgb32f", GL_RGB32F},
+ {0}
+};
+
+static GLint find_fbo_format(const char *name)
+{
+ for (const struct fbo_format *fmt = fbo_formats; fmt->name; fmt++) {
+ if (strcmp(fmt->name, name) == 0)
+ return fmt->format;
+ }
+ return -1;
+}
+
+static int fbo_format_valid(void *arg)
+{
+ return find_fbo_format(*(const char **)arg) >= 0;
+}
+
+static bool can_use_filter_kernel(const struct filter_kernel *kernel)
+{
+ if (!kernel)
+ return false;
+ struct filter_kernel k = *kernel;
+ return mp_init_filter(&k, filter_sizes, 1);
+}
+
+static const char* handle_scaler_opt(const char *name)
+{
+ const struct filter_kernel *kernel = mp_find_filter_kernel(name);
+ if (can_use_filter_kernel(kernel))
+ return kernel->name;
+
+ for (const char **filter = fixed_scale_filters; *filter; filter++) {
+ if (strcmp(*filter, name) == 0)
+ return *filter;
+ }
+
+ return NULL;
+}
+
+static int scaler_valid(void *arg)
+{
+ return handle_scaler_opt(*(const char **)arg) != NULL;
+}
+
+#if 0
+static void print_scalers(void)
+{
+ mp_msg(MSGT_VO, MSGL_INFO, "Available scalers:\n");
+ for (const char **e = fixed_scale_filters; *e; e++) {
+ mp_msg(MSGT_VO, MSGL_INFO, " %s\n", *e);
+ }
+ for (const struct filter_kernel *e = mp_filter_kernels; e->name; e++) {
+ if (can_use_filter_kernel(e))
+ mp_msg(MSGT_VO, MSGL_INFO, " %s\n", e->name);
+ }
+}
+#endif
+
+static bool reparse_cmdline(struct gl_priv *p, char *arg)
+{
+ struct gl_priv tmp = *p->defaults;
+ struct gl_priv *opt = &tmp;
+
+ if (strcmp(arg, "-") == 0) {
+ tmp = *p->orig_cmdline;
+ arg = "";
+ }
+
+ char *scalers[2] = {0};
+ char *fbo_format = NULL;
+
+ const opt_t subopts[] = {
+ {"srgb", OPT_ARG_BOOL, &opt->use_srgb},
+ {"pbo", OPT_ARG_BOOL, &opt->use_pbo},
+ {"glfinish", OPT_ARG_BOOL, &opt->use_glFinish},
+ {"swapinterval", OPT_ARG_INT, &opt->swap_interval},
+ {"osdcolor", OPT_ARG_INT, &opt->osd_color},
+ {"lscale", OPT_ARG_MSTRZ, &scalers[0], scaler_valid},
+ {"cscale", OPT_ARG_MSTRZ, &scalers[1], scaler_valid},
+ {"lparam1", OPT_ARG_FLOAT, &opt->scaler_params[0]},
+ {"lparam2", OPT_ARG_FLOAT, &opt->scaler_params[1]},
+ {"fancy-downscaling", OPT_ARG_BOOL, &opt->use_fancy_downscaling},
+ {"indirect", OPT_ARG_BOOL, &opt->use_indirect},
+ {"scale-sep", OPT_ARG_BOOL, &opt->use_scale_sep},
+ {"fbo-format", OPT_ARG_MSTRZ, &fbo_format, fbo_format_valid},
+ {"dither-depth", OPT_ARG_INT, &opt->dither_depth},
+ {NULL}
+ };
+
+ if (subopt_parse(arg, subopts) != 0)
+ return false;
+
+ p->fbo_format = opt->fbo_format;
+ if (fbo_format)
+ p->fbo_format = find_fbo_format(fbo_format);
+ free(fbo_format);
+
+ for (int n = 0; n < 2; n++) {
+ p->scalers[n].name = opt->scalers[n].name;
+ if (scalers[n])
+ p->scalers[n].name = handle_scaler_opt(scalers[n]);
+ free(scalers[n]);
+ }
+
+ // xxx ideally we'd put all options into an option struct, and just copy
+ p->use_srgb = opt->use_srgb; //xxx changing srgb will be wrong on RGB input!
+ p->use_pbo = opt->use_pbo;
+ p->use_glFinish = opt->use_glFinish;
+ p->swap_interval = opt->swap_interval;
+ p->osd_color = opt->osd_color;
+ memcpy(p->scaler_params, opt->scaler_params, sizeof(p->scaler_params));
+ p->use_fancy_downscaling = opt->use_fancy_downscaling;
+ p->use_indirect = opt->use_indirect;
+ p->use_scale_sep = opt->use_scale_sep;
+ p->dither_depth = opt->dither_depth;
+
+ check_gl_features(p);
+
+ return true;
+}
+
+static int preinit(struct vo *vo, const char *arg)
+{
+ struct gl_priv *p = talloc_zero(vo, struct gl_priv);
+ vo->priv = p;
+
+ bool hq = strcmp(vo->driver->info->short_name, "opengl-hq") == 0;
+
+ *p = (struct gl_priv) {
+ .vo = vo,
+ .colorspace = MP_CSP_DETAILS_DEFAULTS,
+ .use_npot = 1,
+ .use_pbo = hq,
+ .swap_interval = vo_vsync,
+ .osd_color = 0xffffff,
+ .dither_depth = hq ? 0 : -1,
+ .fbo_format = hq ? GL_RGB16 : GL_RGB,
+ .use_scale_sep = 1,
+ .scalers = {
+ { .index = 0, .name = hq ? "lanczos2" : "bilinear" },
+ { .index = 1, .name = "bilinear" },
+ },
+ .scaler_params = {NAN, NAN},
+ .scratch = talloc_zero_array(p, char *, 1),
+ };
+
+ p->defaults = talloc(p, struct gl_priv);
+ *p->defaults = *p;
+
+ char *scalers[2] = {0};
+ char *backend_arg = NULL;
+ char *fbo_format = NULL;
+ char *icc_profile = NULL;
+ char *icc_cache = NULL;
+ int icc_intent = -1;
+ char *icc_size_str = NULL;
+
+ const opt_t subopts[] = {
+ {"gamma", OPT_ARG_BOOL, &p->use_gamma},
+ {"srgb", OPT_ARG_BOOL, &p->use_srgb},
+ {"npot", OPT_ARG_BOOL, &p->use_npot},
+ {"pbo", OPT_ARG_BOOL, &p->use_pbo},
+ {"glfinish", OPT_ARG_BOOL, &p->use_glFinish},
+ {"swapinterval", OPT_ARG_INT, &p->swap_interval},
+ {"osdcolor", OPT_ARG_INT, &p->osd_color},
+ {"stereo", OPT_ARG_INT, &p->stereo_mode},
+ {"lscale", OPT_ARG_MSTRZ, &scalers[0], scaler_valid},
+ {"cscale", OPT_ARG_MSTRZ, &scalers[1], scaler_valid},
+ {"lparam1", OPT_ARG_FLOAT, &p->scaler_params[0]},
+ {"lparam2", OPT_ARG_FLOAT, &p->scaler_params[1]},
+ {"fancy-downscaling", OPT_ARG_BOOL, &p->use_fancy_downscaling},
+ {"debug", OPT_ARG_BOOL, &p->use_gl_debug},
+ {"indirect", OPT_ARG_BOOL, &p->use_indirect},
+ {"scale-sep", OPT_ARG_BOOL, &p->use_scale_sep},
+ {"fbo-format", OPT_ARG_MSTRZ, &fbo_format, fbo_format_valid},
+ {"backend", OPT_ARG_MSTRZ, &backend_arg, backend_valid},
+ {"sw", OPT_ARG_BOOL, &p->allow_sw},
+ {"icc-profile", OPT_ARG_MSTRZ, &icc_profile},
+ {"icc-cache", OPT_ARG_MSTRZ, &icc_cache},
+ {"icc-intent", OPT_ARG_INT, &icc_intent},
+ {"3dlut-size", OPT_ARG_MSTRZ, &icc_size_str,
+ lut3d_size_valid},
+ {"dither-depth", OPT_ARG_INT, &p->dither_depth},
+ {NULL}
+ };
+
+ if (subopt_parse(arg, subopts) != 0) {
+ mp_msg(MSGT_VO, MSGL_FATAL, help_text);
+ goto err_out;
+ }
+
+ int backend = backend_arg ? mpgl_find_backend(backend_arg) : GLTYPE_AUTO;
+ free(backend_arg);
+
+ if (fbo_format)
+ p->fbo_format = find_fbo_format(fbo_format);
+ free(fbo_format);
+
+ for (int n = 0; n < 2; n++) {
+ if (scalers[n])
+ p->scalers[n].name = handle_scaler_opt(scalers[n]);
+ free(scalers[n]);
+ }
+
+ int s_r = 128, s_g = 256, s_b = 64;
+ if (icc_size_str)
+ parse_3dlut_size(icc_size_str, &s_r, &s_g, &s_b);
+ free(icc_size_str);
+
+ bool success = true;
+ if (icc_profile) {
+ success = load_icc(p, icc_profile, icc_cache, icc_intent,
+ s_r, s_g, s_b);
+ }
+ free(icc_profile);
+ free(icc_cache);
+
+ if (!success)
+ goto err_out;
+
+ p->orig_cmdline = talloc(p, struct gl_priv);
+ *p->orig_cmdline = *p;
+
+ p->glctx = mpgl_init(backend, vo);
+ if (!p->glctx)
+ goto err_out;
+ p->gl = p->glctx->gl;
+
+ if (!create_window(p, 320, 200, VOFLAG_HIDDEN))
+ goto err_out;
+ check_gl_features(p);
+ // We created a window to test whether the GL context could be
+ // created and so on. Destroy that window to make sure all state
+ // associated with it is lost.
+ uninit_gl(p);
+ if (!mpgl_destroy_window(p->glctx))
+ goto err_out;
+
+ return 0;
+
+err_out:
+ uninit(vo);
+ return -1;
+}
+
+const struct vo_driver video_out_opengl = {
+ .is_new = true,
+ .info = &(const vo_info_t) {
+ "Extended OpenGL Renderer",
+ "opengl",
+ "Based on vo_gl.c by Reimar Doeffinger",
+ ""
+ },
+ .preinit = preinit,
+ .config = config,
+ .control = control,
+ .draw_slice = draw_slice,
+ .draw_osd = draw_osd,
+ .flip_page = flip_page,
+ .check_events = check_events,
+ .uninit = uninit,
+};
+
+const struct vo_driver video_out_opengl_hq = {
+ .is_new = true,
+ .info = &(const vo_info_t) {
+ "Extended OpenGL Renderer (high quality rendering preset)",
+ "opengl-hq",
+ "Based on vo_gl.c by Reimar Doeffinger",
+ ""
+ },
+ .preinit = preinit,
+ .config = config,
+ .control = control,
+ .draw_slice = draw_slice,
+ .draw_osd = draw_osd,
+ .flip_page = flip_page,
+ .check_events = check_events,
+ .uninit = uninit,
+};
+
+static const char help_text[] =
+"\n--vo=opengl command line help:\n"
+"Example: mpv --vo=opengl:scale-sep:lscale=lanczos2\n"
+"\nOptions:\n"
+" lscale=<filter>\n"
+" Set the scaling filter. Possible choices:\n"
+" bilinear: bilinear texture filtering (fastest).\n"
+" bicubic_fast: bicubic filter (without lookup texture).\n"
+" sharpen3: unsharp masking (sharpening) with radius=3.\n"
+" sharpen5: unsharp masking (sharpening) with radius=5.\n"
+" lanczos2: Lanczos with radius=2 (recommended).\n"
+" lanczos3: Lanczos with radius=3 (not recommended).\n"
+" mitchell: Mitchell-Netravali.\n"
+" Default: bilinear\n"
+" lparam1=<value> / lparam2=<value>\n"
+" Set parameters for configurable filters. Affects chroma scaler\n"
+" as well.\n"
+" Filters which use this:\n"
+" mitchell: b and c params (defaults: b=1/3 c=1/3)\n"
+" kaiser: (defaults: 6.33 6.33)\n"
+" sharpen3: lparam1 sets sharpening strength (default: 0.5)\n"
+" sharpen5: as with sharpen3\n"
+" osdcolor=<0xAARRGGBB>\n"
+" Use the given color for the OSD.\n"
+" stereo=<n>\n"
+" 0: normal display\n"
+" 1: side-by-side to red-cyan stereo\n"
+" 2: side-by-side to green-magenta stereo\n"
+" 3: side-by-side to quadbuffer stereo\n"
+" srgb\n"
+" Enable gamma-correct scaling by working in linear light. This\n"
+" makes use of sRGB textures and framebuffers.\n"
+" This option forces the options 'indirect' and 'gamma'.\n"
+" NOTE: For YUV colorspaces, gamma 2.2 is assumed. RGB input is always\n"
+" assumed to be in sRGB.\n"
+" pbo\n"
+" Enable use of PBOs. This is faster, but can sometimes lead to\n"
+" sporadic and temporary image corruption.\n"
+" dither-depth=<n>\n"
+" Positive non-zero values select the target bit depth.\n"
+" -1: Disable any dithering done by mpv.\n"
+" 0: Automatic selection. If output bit depth can't be detected,\n"
+" 8 bits per component are assumed.\n"
+" 8: Dither to 8 bit output.\n"
+" Default: -1.\n"
+" Note that dithering will always be disabled if the bit depth\n"
+" of the video is lower or qual to the detected dither-depth.\n"
+" If color management is enabled, input depth is assumed to be\n"
+" 16 bits, because the 3D LUT output is 16 bit wide.\n"
+" debug\n"
+" Check for OpenGL errors, i.e. call glGetError(). Also request a\n"
+" debug OpenGL context.\n"
+"Less useful options:\n"
+" swapinterval=<n>\n"
+" Interval in displayed frames between to buffer swaps.\n"
+" 1 is equivalent to enable VSYNC, 0 to disable VSYNC.\n"
+" no-scale-sep\n"
+" When using a separable scale filter for luma, usually two filter\n"
+" passes are done. This is often faster. However, it forces\n"
+" conversion to RGB in an extra pass, so it can actually be slower\n"
+" if used with fast filters on small screen resolutions. Using\n"
+" this options will make rendering a single operation.\n"
+" Note that chroma scalers are always done as 1-pass filters.\n"
+" cscale=<n>\n"
+" As lscale but for chroma (2x slower with little visible effect).\n"
+" Note that with some scaling filters, upscaling is always done in\n"
+" RGB. If chroma is not subsampled, this option is ignored, and the\n"
+" luma scaler is used instead. Setting this option is often useless.\n"
+" fancy-downscaling\n"
+" When using convolution based filters, extend the filter size\n"
+" when downscaling. Trades quality for reduced downscaling performance.\n"
+" no-npot\n"
+" Force use of power-of-2 texture sizes. For debugging only.\n"
+" Borders will look discolored due to filtering.\n"
+" glfinish\n"
+" Call glFinish() before swapping buffers\n"
+" backend=<sys>\n"
+" auto: auto-select (default)\n"
+" cocoa: Cocoa/OSX\n"
+" win: Win32/WGL\n"
+" x11: X11/GLX\n"
+" indirect\n"
+" Do YUV conversion and scaling as separate passes. This will\n"
+" first render the video into a video-sized RGB texture, and\n"
+" draw the result on screen. The luma scaler is used to scale\n"
+" the RGB image when rendering to screen. The chroma scaler\n"
+" is used only on YUV conversion, and only if the video uses\n"
+" chroma-subsampling.\n"
+" This mechanism is disabled on RGB input.\n"
+" fbo-format=<fmt>\n"
+" Selects the internal format of any FBO textures used.\n"
+" fmt can be one of: rgb, rgba, rgb8, rgb10, rgb16, rgb16f, rgb32f\n"
+" Default: rgb.\n"
+" gamma\n"
+" Always enable gamma control. (Disables delayed enabling.)\n"
+"Color management:\n"
+" icc-profile=<file>\n"
+" Load an ICC profile and use it to transform linear RGB to\n"
+" screen output. Needs LittleCMS2 support compiled in.\n"
+" icc-cache=<file>\n"
+" Store and load the 3D LUT created from the ICC profile in\n"
+" this file. This can be used to speed up loading, since\n"
+" LittleCMS2 can take a while to create the 3D LUT.\n"
+" Note that this file will be up to ~100 MB big.\n"
+" icc-intent=<value>\n"
+" 0: perceptual\n"
+" 1: relative colorimetric\n"
+" 2: saturation\n"
+" 3: absolute colorimetric (default)\n"
+" 3dlut-size=<r>x<g>x<b>\n"
+" Size of the 3D LUT generated from the ICC profile in each\n"
+" dimension. Default is 128x256x64.\n"
+" Sizes must be a power of two, and 256 at most.\n"
+"Note: all defaults mentioned are for 'opengl', not 'opengl-hq'.\n"
+"\n";
diff --git a/video/out/vo_opengl_old.c b/video/out/vo_opengl_old.c
new file mode 100644
index 0000000000..b8b1fd4813
--- /dev/null
+++ b/video/out/vo_opengl_old.c
@@ -0,0 +1,1166 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <stdbool.h>
+#include <assert.h>
+
+#include "config.h"
+#include "talloc.h"
+#include "mp_msg.h"
+#include "subopt-helper.h"
+#include "video_out.h"
+#include "libmpcodecs/vfcap.h"
+#include "libmpcodecs/mp_image.h"
+#include "geometry.h"
+#include "sub/sub.h"
+
+#include "gl_common.h"
+#include "gl_osd.h"
+#include "aspect.h"
+#include "fastmemcpy.h"
+
+//for gl_priv.use_yuv
+#define MASK_ALL_YUV (~(1 << YUV_CONVERSION_NONE))
+#define MASK_NOT_COMBINERS (~((1 << YUV_CONVERSION_NONE) | (1 << YUV_CONVERSION_COMBINERS)))
+#define MASK_GAMMA_SUPPORT (MASK_NOT_COMBINERS & ~(1 << YUV_CONVERSION_FRAGMENT))
+
+struct gl_priv {
+ MPGLContext *glctx;
+ GL *gl;
+
+ int allow_sw;
+
+ int use_osd;
+ int scaled_osd;
+ struct mpgl_osd *osd;
+ int osd_color;
+
+ int use_ycbcr;
+ int use_yuv;
+ struct mp_csp_details colorspace;
+ int is_yuv;
+ int lscale;
+ int cscale;
+ float filter_strength;
+ float noise_strength;
+ int yuvconvtype;
+ int use_rectangle;
+ int err_shown;
+ uint32_t image_width;
+ uint32_t image_height;
+ uint32_t image_format;
+ int many_fmts;
+ int have_texture_rg;
+ int ati_hack;
+ int force_pbo;
+ int use_glFinish;
+ int swap_interval;
+ GLenum target;
+ GLint texfmt;
+ GLenum gl_format;
+ GLenum gl_type;
+ GLuint buffer;
+ GLuint buffer_uv[2];
+ int buffersize;
+ int buffersize_uv;
+ void *bufferptr;
+ void *bufferptr_uv[2];
+ GLuint fragprog;
+ GLuint default_texs[22];
+ char *custom_prog;
+ char *custom_tex;
+ int custom_tlin;
+ int custom_trect;
+ int mipmap_gen;
+ int stereo_mode;
+
+ struct mp_csp_equalizer video_eq;
+
+ int texture_width;
+ int texture_height;
+ int mpi_flipped;
+ int vo_flipped;
+ int ass_border_x, ass_border_y;
+
+ unsigned int slice_height;
+};
+
+static void resize(struct vo *vo, int x, int y)
+{
+ struct gl_priv *p = vo->priv;
+ GL *gl = p->gl;
+
+ mp_msg(MSGT_VO, MSGL_V, "[gl] Resize: %dx%d\n", x, y);
+ gl->Viewport(0, 0, x, y);
+
+ gl->MatrixMode(GL_PROJECTION);
+ gl->LoadIdentity();
+ p->ass_border_x = p->ass_border_y = 0;
+ if (aspect_scaling()) {
+ int new_w, new_h;
+ GLdouble scale_x, scale_y;
+ aspect(vo, &new_w, &new_h, A_WINZOOM);
+ panscan_calc_windowed(vo);
+ new_w += vo->panscan_x;
+ new_h += vo->panscan_y;
+ scale_x = (GLdouble)new_w / (GLdouble)x;
+ scale_y = (GLdouble)new_h / (GLdouble)y;
+ gl->Scaled(scale_x, scale_y, 1);
+ p->ass_border_x = (vo->dwidth - new_w) / 2;
+ p->ass_border_y = (vo->dheight - new_h) / 2;
+ }
+ gl->Ortho(0, p->image_width, p->image_height, 0, -1, 1);
+
+ gl->MatrixMode(GL_MODELVIEW);
+ gl->LoadIdentity();
+
+ gl->Clear(GL_COLOR_BUFFER_BIT);
+ vo->want_redraw = true;
+}
+
+static void texSize(struct vo *vo, int w, int h, int *texw, int *texh)
+{
+ struct gl_priv *p = vo->priv;
+
+ if (p->use_rectangle) {
+ *texw = w;
+ *texh = h;
+ } else {
+ *texw = 32;
+ while (*texw < w)
+ *texw *= 2;
+ *texh = 32;
+ while (*texh < h)
+ *texh *= 2;
+ }
+ if (p->ati_hack)
+ *texw = (*texw + 511) & ~511;
+}
+
+//! maximum size of custom fragment program
+#define MAX_CUSTOM_PROG_SIZE (1024 * 1024)
+static void update_yuvconv(struct vo *vo)
+{
+ struct gl_priv *p = vo->priv;
+ GL *gl = p->gl;
+
+ int xs, ys, depth;
+ struct mp_csp_params cparams = { .colorspace = p->colorspace };
+ mp_csp_copy_equalizer_values(&cparams, &p->video_eq);
+ gl_conversion_params_t params = {
+ p->target, p->yuvconvtype, cparams,
+ p->texture_width, p->texture_height, 0, 0, p->filter_strength,
+ p->noise_strength
+ };
+ mp_get_chroma_shift(p->image_format, &xs, &ys, &depth);
+ params.chrom_texw = params.texw >> xs;
+ params.chrom_texh = params.texh >> ys;
+ params.csp_params.input_bits = depth;
+ params.csp_params.texture_bits = depth+7 & ~7;
+ glSetupYUVConversion(gl, &params);
+ if (p->custom_prog) {
+ FILE *f = fopen(p->custom_prog, "rb");
+ if (!f) {
+ mp_msg(MSGT_VO, MSGL_WARN,
+ "[gl] Could not read customprog %s\n", p->custom_prog);
+ } else {
+ char *prog = calloc(1, MAX_CUSTOM_PROG_SIZE + 1);
+ fread(prog, 1, MAX_CUSTOM_PROG_SIZE, f);
+ fclose(f);
+ loadGPUProgram(gl, GL_FRAGMENT_PROGRAM, prog);
+ free(prog);
+ }
+ gl->ProgramEnvParameter4f(GL_FRAGMENT_PROGRAM, 0,
+ 1.0 / p->texture_width,
+ 1.0 / p->texture_height,
+ p->texture_width, p->texture_height);
+ }
+ if (p->custom_tex) {
+ FILE *f = fopen(p->custom_tex, "rb");
+ if (!f) {
+ mp_msg(MSGT_VO, MSGL_WARN,
+ "[gl] Could not read customtex %s\n", p->custom_tex);
+ } else {
+ int width, height, maxval;
+ gl->ActiveTexture(GL_TEXTURE3);
+ if (glCreatePPMTex(gl, p->custom_trect ? GL_TEXTURE_RECTANGLE : GL_TEXTURE_2D,
+ 0, p->custom_tlin ? GL_LINEAR : GL_NEAREST,
+ f, &width, &height, &maxval)) {
+ gl->ProgramEnvParameter4f(GL_FRAGMENT_PROGRAM, 1,
+ 1.0 / width, 1.0 / height,
+ width, height);
+ } else
+ mp_msg(MSGT_VO, MSGL_WARN,
+ "[gl] Error parsing customtex %s\n", p->custom_tex);
+ fclose(f);
+ gl->ActiveTexture(GL_TEXTURE0);
+ }
+ }
+}
+
+static void draw_osd(struct vo *vo, struct osd_state *osd)
+{
+ struct gl_priv *p = vo->priv;
+ GL *gl = p->gl;
+ assert(p->osd);
+
+ if (!p->use_osd)
+ return;
+
+ if (!p->scaled_osd) {
+ gl->MatrixMode(GL_PROJECTION);
+ gl->PushMatrix();
+ gl->LoadIdentity();
+ gl->Ortho(0, vo->dwidth, vo->dheight, 0, -1, 1);
+ }
+
+ gl->Color4ub((p->osd_color >> 16) & 0xff, (p->osd_color >> 8) & 0xff,
+ p->osd_color & 0xff, 0xff - (p->osd_color >> 24));
+
+ struct mp_osd_res res = {
+ .w = vo->dwidth,
+ .h = vo->dheight,
+ .display_par = vo->monitor_par,
+ .video_par = vo->aspdat.par,
+ };
+ if (p->scaled_osd) {
+ res.w = p->image_width;
+ res.h = p->image_height;
+ } else if (aspect_scaling()) {
+ res.ml = res.mr = p->ass_border_x;
+ res.mt = res.mb = p->ass_border_y;
+ }
+
+ mpgl_osd_draw_legacy(p->osd, osd, res);
+
+ if (!p->scaled_osd)
+ gl->PopMatrix();
+}
+
+/**
+ * \brief uninitialize OpenGL context, freeing textures, buffers etc.
+ */
+static void uninitGl(struct vo *vo)
+{
+ struct gl_priv *p = vo->priv;
+ GL *gl = p->gl;
+
+ if (!gl)
+ return;
+
+ int i = 0;
+ if (gl->DeletePrograms && p->fragprog)
+ gl->DeletePrograms(1, &p->fragprog);
+ p->fragprog = 0;
+ while (p->default_texs[i] != 0)
+ i++;
+ if (i)
+ gl->DeleteTextures(i, p->default_texs);
+ p->default_texs[0] = 0;
+ if (p->osd)
+ mpgl_osd_destroy(p->osd);
+ p->osd = NULL;
+ p->buffer = 0;
+ p->buffersize = 0;
+ p->bufferptr = NULL;
+ if (gl->DeleteBuffers && p->buffer_uv[0])
+ gl->DeleteBuffers(2, p->buffer_uv);
+ p->buffer_uv[0] = p->buffer_uv[1] = 0;
+ p->buffersize_uv = 0;
+ p->bufferptr_uv[0] = p->bufferptr_uv[1] = 0;
+ p->err_shown = 0;
+}
+
+static void autodetectGlExtensions(struct vo *vo)
+{
+ struct gl_priv *p = vo->priv;
+ GL *gl = p->gl;
+
+ const char *extensions = gl->GetString(GL_EXTENSIONS);
+ const char *vendor = gl->GetString(GL_VENDOR);
+ const char *version = gl->GetString(GL_VERSION);
+ const char *renderer = gl->GetString(GL_RENDERER);
+ int is_ati = vendor && strstr(vendor, "ATI") != NULL;
+ int ati_broken_pbo = 0;
+ mp_msg(MSGT_VO, MSGL_V, "[gl] Running on OpenGL '%s' by '%s', version '%s'\n",
+ renderer, vendor, version);
+ if (is_ati && strncmp(version, "2.1.", 4) == 0) {
+ int ver = atoi(version + 4);
+ mp_msg(MSGT_VO, MSGL_V, "[gl] Detected ATI driver version: %i\n", ver);
+ ati_broken_pbo = ver && ver < 8395;
+ }
+ if (p->ati_hack == -1)
+ p->ati_hack = ati_broken_pbo;
+ if (p->force_pbo == -1) {
+ p->force_pbo = 0;
+ if (extensions && strstr(extensions, "_pixel_buffer_object"))
+ p->force_pbo = is_ati;
+ }
+ p->have_texture_rg = extensions && strstr(extensions, "GL_ARB_texture_rg");
+ if (p->use_rectangle == -1) {
+ p->use_rectangle = 0;
+ if (extensions) {
+// if (strstr(extensions, "_texture_non_power_of_two"))
+ if (strstr(extensions, "_texture_rectangle"))
+ p->use_rectangle = renderer
+ && strstr(renderer, "Mesa DRI R200") ? 1 : 0;
+ }
+ }
+ if (p->use_osd == -1)
+ p->use_osd = gl->BindTexture != NULL;
+ if (p->use_yuv == -1)
+ p->use_yuv = glAutodetectYUVConversion(gl);
+
+ int eq_caps = 0;
+ int yuv_mask = (1 << p->use_yuv);
+ if (!(yuv_mask & MASK_NOT_COMBINERS)) {
+ // combiners
+ eq_caps = (1 << MP_CSP_EQ_HUE) | (1 << MP_CSP_EQ_SATURATION);
+ } else if (yuv_mask & MASK_ALL_YUV) {
+ eq_caps = MP_CSP_EQ_CAPS_COLORMATRIX;
+ if (yuv_mask & MASK_GAMMA_SUPPORT)
+ eq_caps |= MP_CSP_EQ_CAPS_GAMMA;
+ }
+ p->video_eq.capabilities = eq_caps;
+
+ if (is_ati && (p->lscale == 1 || p->lscale == 2 || p->cscale == 1 || p->cscale == 2))
+ mp_msg(MSGT_VO, MSGL_WARN, "[gl] Selected scaling mode may be broken on"
+ " ATI cards.\n"
+ "Tell _them_ to fix GL_REPEAT if you have issues.\n");
+ mp_msg(MSGT_VO, MSGL_V, "[gl] Settings after autodetection: ati-hack = %i, "
+ "force-pbo = %i, rectangle = %i, yuv = %i\n",
+ p->ati_hack, p->force_pbo, p->use_rectangle, p->use_yuv);
+}
+
+static GLint get_scale_type(struct vo *vo, int chroma)
+{
+ struct gl_priv *p = vo->priv;
+
+ int nearest = (chroma ? p->cscale : p->lscale) & 64;
+ if (nearest)
+ return p->mipmap_gen ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST;
+ return p->mipmap_gen ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR;
+}
+
+// Return the high byte of the value that represents white in chroma (U/V)
+static int get_chroma_clear_val(int bit_depth)
+{
+ return 1 << (bit_depth - 1 & 7);
+}
+
+/**
+ * \brief Initialize a (new or reused) OpenGL context.
+ * set global gl-related variables to their default values
+ */
+static int initGl(struct vo *vo, uint32_t d_width, uint32_t d_height)
+{
+ struct gl_priv *p = vo->priv;
+ GL *gl = p->gl;
+
+ GLint scale_type = get_scale_type(vo, 0);
+ autodetectGlExtensions(vo);
+ p->target = p->use_rectangle == 1 ? GL_TEXTURE_RECTANGLE : GL_TEXTURE_2D;
+ p->yuvconvtype = SET_YUV_CONVERSION(p->use_yuv) |
+ SET_YUV_LUM_SCALER(p->lscale) |
+ SET_YUV_CHROM_SCALER(p->cscale);
+
+ texSize(vo, p->image_width, p->image_height,
+ &p->texture_width, &p->texture_height);
+
+ gl->Disable(GL_BLEND);
+ gl->Disable(GL_DEPTH_TEST);
+ gl->DepthMask(GL_FALSE);
+ gl->Disable(GL_CULL_FACE);
+ gl->Enable(p->target);
+ gl->DrawBuffer(GL_BACK);
+ gl->TexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ mp_msg(MSGT_VO, MSGL_V, "[gl] Creating %dx%d texture...\n",
+ p->texture_width, p->texture_height);
+
+ glCreateClearTex(gl, p->target, p->texfmt, p->gl_format,
+ p->gl_type, scale_type,
+ p->texture_width, p->texture_height, 0);
+
+ if (p->mipmap_gen)
+ gl->TexParameteri(p->target, GL_GENERATE_MIPMAP, GL_TRUE);
+
+ if (p->is_yuv) {
+ int i;
+ int xs, ys, depth;
+ scale_type = get_scale_type(vo, 1);
+ mp_get_chroma_shift(p->image_format, &xs, &ys, &depth);
+ int clear = get_chroma_clear_val(depth);
+ gl->GenTextures(21, p->default_texs);
+ p->default_texs[21] = 0;
+ for (i = 0; i < 7; i++) {
+ gl->ActiveTexture(GL_TEXTURE1 + i);
+ gl->BindTexture(GL_TEXTURE_2D, p->default_texs[i]);
+ gl->BindTexture(GL_TEXTURE_RECTANGLE, p->default_texs[i + 7]);
+ gl->BindTexture(GL_TEXTURE_3D, p->default_texs[i + 14]);
+ }
+ gl->ActiveTexture(GL_TEXTURE1);
+ glCreateClearTex(gl, p->target, p->texfmt, p->gl_format,
+ p->gl_type, scale_type,
+ p->texture_width >> xs, p->texture_height >> ys,
+ clear);
+ if (p->mipmap_gen)
+ gl->TexParameteri(p->target, GL_GENERATE_MIPMAP, GL_TRUE);
+ gl->ActiveTexture(GL_TEXTURE2);
+ glCreateClearTex(gl, p->target, p->texfmt, p->gl_format,
+ p->gl_type, scale_type,
+ p->texture_width >> xs, p->texture_height >> ys,
+ clear);
+ if (p->mipmap_gen)
+ gl->TexParameteri(p->target, GL_GENERATE_MIPMAP, GL_TRUE);
+ gl->ActiveTexture(GL_TEXTURE0);
+ gl->BindTexture(p->target, 0);
+ }
+ if (p->is_yuv || p->custom_prog) {
+ if ((MASK_NOT_COMBINERS & (1 << p->use_yuv)) || p->custom_prog) {
+ if (!gl->GenPrograms || !gl->BindProgram)
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "[gl] fragment program functions missing!\n");
+ else {
+ gl->GenPrograms(1, &p->fragprog);
+ gl->BindProgram(GL_FRAGMENT_PROGRAM, p->fragprog);
+ }
+ }
+ update_yuvconv(vo);
+ }
+
+ p->osd = mpgl_osd_init(gl, true);
+ p->osd->scaled = p->scaled_osd;
+
+ resize(vo, d_width, d_height);
+
+ gl->ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ gl->Clear(GL_COLOR_BUFFER_BIT);
+ if (gl->SwapInterval && p->swap_interval >= 0)
+ gl->SwapInterval(p->swap_interval);
+ return 1;
+}
+
+static bool create_window(struct vo *vo, uint32_t d_width, uint32_t d_height,
+ uint32_t flags)
+{
+ struct gl_priv *p = vo->priv;
+
+ if (p->stereo_mode == GL_3D_QUADBUFFER)
+ flags |= VOFLAG_STEREO;
+
+ int mpgl_caps = MPGL_CAP_GL_LEGACY;
+ if (!p->allow_sw)
+ mpgl_caps |= MPGL_CAP_NO_SW;
+ return mpgl_create_window(p->glctx, mpgl_caps, d_width, d_height, flags);
+}
+
+static int config(struct vo *vo, uint32_t width, uint32_t height,
+ uint32_t d_width, uint32_t d_height, uint32_t flags,
+ uint32_t format)
+{
+ struct gl_priv *p = vo->priv;
+
+ int xs, ys;
+ p->image_height = height;
+ p->image_width = width;
+ p->image_format = format;
+ p->is_yuv = mp_get_chroma_shift(p->image_format, &xs, &ys, NULL) > 0;
+ p->is_yuv |= (xs << 8) | (ys << 16);
+ glFindFormat(format, p->have_texture_rg, NULL, &p->texfmt, &p->gl_format,
+ &p->gl_type);
+
+ p->vo_flipped = !!(flags & VOFLAG_FLIPPING);
+
+ if (vo->config_count)
+ uninitGl(vo);
+
+ if (!create_window(vo, d_width, d_height, flags))
+ return -1;
+
+ initGl(vo, vo->dwidth, vo->dheight);
+
+ return 0;
+}
+
+static void check_events(struct vo *vo)
+{
+ struct gl_priv *p = vo->priv;
+
+ int e = p->glctx->check_events(vo);
+ if (e & VO_EVENT_REINIT) {
+ uninitGl(vo);
+ initGl(vo, vo->dwidth, vo->dheight);
+ }
+ if (e & VO_EVENT_RESIZE)
+ resize(vo, vo->dwidth, vo->dheight);
+ if (e & VO_EVENT_EXPOSE)
+ vo->want_redraw = true;
+}
+
+static void do_render(struct vo *vo)
+{
+ struct gl_priv *p = vo->priv;
+ GL *gl = p->gl;
+
+// Enable(GL_TEXTURE_2D);
+// BindTexture(GL_TEXTURE_2D, texture_id);
+
+ gl->Color4f(1, 1, 1, 1);
+ if (p->is_yuv || p->custom_prog)
+ glEnableYUVConversion(gl, p->target, p->yuvconvtype);
+ if (p->stereo_mode) {
+ glEnable3DLeft(gl, p->stereo_mode);
+ glDrawTex(gl, 0, 0, p->image_width, p->image_height,
+ 0, 0, p->image_width >> 1, p->image_height,
+ p->texture_width, p->texture_height,
+ p->use_rectangle == 1, p->is_yuv,
+ p->mpi_flipped ^ p->vo_flipped);
+ glEnable3DRight(gl, p->stereo_mode);
+ glDrawTex(gl, 0, 0, p->image_width, p->image_height,
+ p->image_width >> 1, 0, p->image_width >> 1,
+ p->image_height, p->texture_width, p->texture_height,
+ p->use_rectangle == 1, p->is_yuv,
+ p->mpi_flipped ^ p->vo_flipped);
+ glDisable3D(gl, p->stereo_mode);
+ } else {
+ glDrawTex(gl, 0, 0, p->image_width, p->image_height,
+ 0, 0, p->image_width, p->image_height,
+ p->texture_width, p->texture_height,
+ p->use_rectangle == 1, p->is_yuv,
+ p->mpi_flipped ^ p->vo_flipped);
+ }
+ if (p->is_yuv || p->custom_prog)
+ glDisableYUVConversion(gl, p->target, p->yuvconvtype);
+}
+
+static void flip_page(struct vo *vo)
+{
+ struct gl_priv *p = vo->priv;
+ GL *gl = p->gl;
+
+ if (p->use_glFinish)
+ gl->Finish();
+ p->glctx->swapGlBuffers(p->glctx);
+ if (aspect_scaling())
+ gl->Clear(GL_COLOR_BUFFER_BIT);
+}
+
+static int draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h,
+ int x, int y)
+{
+ struct gl_priv *p = vo->priv;
+ GL *gl = p->gl;
+
+ p->mpi_flipped = stride[0] < 0;
+ glUploadTex(gl, p->target, p->gl_format, p->gl_type, src[0], stride[0],
+ x, y, w, h, p->slice_height);
+ if (p->is_yuv) {
+ int xs, ys;
+ mp_get_chroma_shift(p->image_format, &xs, &ys, NULL);
+ gl->ActiveTexture(GL_TEXTURE1);
+ glUploadTex(gl, p->target, p->gl_format, p->gl_type, src[1], stride[1],
+ x >> xs, y >> ys, w >> xs, h >> ys, p->slice_height);
+ gl->ActiveTexture(GL_TEXTURE2);
+ glUploadTex(gl, p->target, p->gl_format, p->gl_type, src[2], stride[2],
+ x >> xs, y >> ys, w >> xs, h >> ys, p->slice_height);
+ gl->ActiveTexture(GL_TEXTURE0);
+ }
+ return 0;
+}
+
+static uint32_t get_image(struct vo *vo, mp_image_t *mpi)
+{
+ struct gl_priv *p = vo->priv;
+ GL *gl = p->gl;
+
+ int needed_size;
+ if (!gl->GenBuffers || !gl->BindBuffer || !gl->BufferData || !gl->MapBuffer) {
+ if (!p->err_shown)
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] extensions missing for dr\n"
+ "Expect a _major_ speed penalty\n");
+ p->err_shown = 1;
+ return VO_FALSE;
+ }
+ if (mpi->flags & MP_IMGFLAG_READABLE)
+ return VO_FALSE;
+ if (mpi->type != MP_IMGTYPE_STATIC && mpi->type != MP_IMGTYPE_TEMP &&
+ (mpi->type != MP_IMGTYPE_NUMBERED || mpi->number))
+ return VO_FALSE;
+ if (p->ati_hack) {
+ mpi->width = p->texture_width;
+ mpi->height = p->texture_height;
+ }
+ mpi->stride[0] = mpi->width * mpi->bpp / 8;
+ needed_size = mpi->stride[0] * mpi->height;
+ if (!p->buffer)
+ gl->GenBuffers(1, &p->buffer);
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->buffer);
+ if (needed_size > p->buffersize) {
+ p->buffersize = needed_size;
+ gl->BufferData(GL_PIXEL_UNPACK_BUFFER, p->buffersize,
+ NULL, GL_DYNAMIC_DRAW);
+ }
+ if (!p->bufferptr)
+ p->bufferptr = gl->MapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
+ mpi->planes[0] = p->bufferptr;
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+ if (!mpi->planes[0]) {
+ if (!p->err_shown)
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] could not acquire buffer for dr\n"
+ "Expect a _major_ speed penalty\n");
+ p->err_shown = 1;
+ return VO_FALSE;
+ }
+ if (p->is_yuv) {
+ // planar YUV
+ int xs, ys, component_bits;
+ mp_get_chroma_shift(p->image_format, &xs, &ys, &component_bits);
+ int bp = (component_bits + 7) / 8;
+ mpi->flags |= MP_IMGFLAG_COMMON_STRIDE | MP_IMGFLAG_COMMON_PLANE;
+ mpi->stride[0] = mpi->width * bp;
+ mpi->planes[1] = mpi->planes[0] + mpi->stride[0] * mpi->height;
+ mpi->stride[1] = (mpi->width >> xs) * bp;
+ mpi->planes[2] = mpi->planes[1] + mpi->stride[1] * (mpi->height >> ys);
+ mpi->stride[2] = (mpi->width >> xs) * bp;
+ if (p->ati_hack) {
+ mpi->flags &= ~MP_IMGFLAG_COMMON_PLANE;
+ if (!p->buffer_uv[0])
+ gl->GenBuffers(2, p->buffer_uv);
+ int buffer_size = mpi->stride[1] * mpi->height;
+ if (buffer_size > p->buffersize_uv) {
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->buffer_uv[0]);
+ gl->BufferData(GL_PIXEL_UNPACK_BUFFER, buffer_size, NULL,
+ GL_DYNAMIC_DRAW);
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->buffer_uv[1]);
+ gl->BufferData(GL_PIXEL_UNPACK_BUFFER, buffer_size, NULL,
+ GL_DYNAMIC_DRAW);
+ p->buffersize_uv = buffer_size;
+ }
+ if (!p->bufferptr_uv[0]) {
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->buffer_uv[0]);
+ p->bufferptr_uv[0] = gl->MapBuffer(GL_PIXEL_UNPACK_BUFFER,
+ GL_WRITE_ONLY);
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->buffer_uv[1]);
+ p->bufferptr_uv[1] = gl->MapBuffer(GL_PIXEL_UNPACK_BUFFER,
+ GL_WRITE_ONLY);
+ }
+ mpi->planes[1] = p->bufferptr_uv[0];
+ mpi->planes[2] = p->bufferptr_uv[1];
+ }
+ }
+ mpi->flags |= MP_IMGFLAG_DIRECT;
+ return VO_TRUE;
+}
+
+static void clear_border(struct vo *vo, uint8_t *dst, int start, int stride,
+ int height, int full_height, int value)
+{
+ int right_border = stride - start;
+ int bottom_border = full_height - height;
+ while (height > 0) {
+ if (right_border > 0)
+ memset(dst + start, value, right_border);
+ dst += stride;
+ height--;
+ }
+ if (bottom_border > 0)
+ memset(dst, value, stride * bottom_border);
+}
+
+static uint32_t draw_image(struct vo *vo, mp_image_t *mpi)
+{
+ struct gl_priv *p = vo->priv;
+ GL *gl = p->gl;
+
+ int slice = p->slice_height;
+ int stride[3];
+ unsigned char *planes[3];
+ mp_image_t mpi2 = *mpi;
+ int w = mpi->w, h = mpi->h;
+ if (mpi->flags & MP_IMGFLAG_DRAW_CALLBACK)
+ goto skip_upload;
+ mpi2.flags = 0;
+ mpi2.type = MP_IMGTYPE_TEMP;
+ mpi2.width = mpi2.w;
+ mpi2.height = mpi2.h;
+ if (p->force_pbo && !(mpi->flags & MP_IMGFLAG_DIRECT) && !p->bufferptr
+ && get_image(vo, &mpi2) == VO_TRUE)
+ {
+ int bp = mpi->bpp / 8;
+ int xs, ys, component_bits;
+ mp_get_chroma_shift(p->image_format, &xs, &ys, &component_bits);
+ if (p->is_yuv)
+ bp = (component_bits + 7) / 8;
+ memcpy_pic(mpi2.planes[0], mpi->planes[0], mpi->w * bp, mpi->h,
+ mpi2.stride[0], mpi->stride[0]);
+ int uv_bytes = (mpi->w >> xs) * bp;
+ if (p->is_yuv) {
+ memcpy_pic(mpi2.planes[1], mpi->planes[1], uv_bytes, mpi->h >> ys,
+ mpi2.stride[1], mpi->stride[1]);
+ memcpy_pic(mpi2.planes[2], mpi->planes[2], uv_bytes, mpi->h >> ys,
+ mpi2.stride[2], mpi->stride[2]);
+ }
+ if (p->ati_hack) {
+ // since we have to do a full upload we need to clear the borders
+ clear_border(vo, mpi2.planes[0], mpi->w * bp, mpi2.stride[0],
+ mpi->h, mpi2.height, 0);
+ if (p->is_yuv) {
+ int clear = get_chroma_clear_val(component_bits);
+ clear_border(vo, mpi2.planes[1], uv_bytes, mpi2.stride[1],
+ mpi->h >> ys, mpi2.height >> ys, clear);
+ clear_border(vo, mpi2.planes[2], uv_bytes, mpi2.stride[2],
+ mpi->h >> ys, mpi2.height >> ys, clear);
+ }
+ }
+ mpi = &mpi2;
+ }
+ stride[0] = mpi->stride[0];
+ stride[1] = mpi->stride[1];
+ stride[2] = mpi->stride[2];
+ planes[0] = mpi->planes[0];
+ planes[1] = mpi->planes[1];
+ planes[2] = mpi->planes[2];
+ p->mpi_flipped = stride[0] < 0;
+ if (mpi->flags & MP_IMGFLAG_DIRECT) {
+ intptr_t base = (intptr_t)planes[0];
+ if (p->ati_hack) {
+ w = p->texture_width;
+ h = p->texture_height;
+ }
+ if (p->mpi_flipped)
+ base += (mpi->h - 1) * stride[0];
+ planes[0] -= base;
+ planes[1] -= base;
+ planes[2] -= base;
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->buffer);
+ gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
+ p->bufferptr = NULL;
+ if (!(mpi->flags & MP_IMGFLAG_COMMON_PLANE))
+ planes[0] = planes[1] = planes[2] = NULL;
+ slice = 0; // always "upload" full texture
+ }
+ glUploadTex(gl, p->target, p->gl_format, p->gl_type, planes[0],
+ stride[0], 0, 0, w, h, slice);
+ if (p->is_yuv) {
+ int xs, ys;
+ mp_get_chroma_shift(p->image_format, &xs, &ys, NULL);
+ if ((mpi->flags & MP_IMGFLAG_DIRECT) && !(mpi->flags & MP_IMGFLAG_COMMON_PLANE)) {
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->buffer_uv[0]);
+ gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
+ p->bufferptr_uv[0] = NULL;
+ }
+ gl->ActiveTexture(GL_TEXTURE1);
+ glUploadTex(gl, p->target, p->gl_format, p->gl_type, planes[1],
+ stride[1], 0, 0, w >> xs, h >> ys, slice);
+ if ((mpi->flags & MP_IMGFLAG_DIRECT) && !(mpi->flags & MP_IMGFLAG_COMMON_PLANE)) {
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->buffer_uv[1]);
+ gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
+ p->bufferptr_uv[1] = NULL;
+ }
+ gl->ActiveTexture(GL_TEXTURE2);
+ glUploadTex(gl, p->target, p->gl_format, p->gl_type, planes[2],
+ stride[2], 0, 0, w >> xs, h >> ys, slice);
+ gl->ActiveTexture(GL_TEXTURE0);
+ }
+ if (mpi->flags & MP_IMGFLAG_DIRECT) {
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+ }
+skip_upload:
+ do_render(vo);
+ return VO_TRUE;
+}
+
+static mp_image_t *get_screenshot(struct vo *vo)
+{
+ struct gl_priv *p = vo->priv;
+ GL *gl = p->gl;
+
+ mp_image_t *image = alloc_mpi(p->texture_width, p->texture_height,
+ p->image_format);
+
+ glDownloadTex(gl, p->target, p->gl_format, p->gl_type, image->planes[0],
+ image->stride[0]);
+
+ if (p->is_yuv) {
+ gl->ActiveTexture(GL_TEXTURE1);
+ glDownloadTex(gl, p->target, p->gl_format, p->gl_type, image->planes[1],
+ image->stride[1]);
+ gl->ActiveTexture(GL_TEXTURE2);
+ glDownloadTex(gl, p->target, p->gl_format, p->gl_type, image->planes[2],
+ image->stride[2]);
+ gl->ActiveTexture(GL_TEXTURE0);
+ }
+
+ image->w = p->image_width;
+ image->h = p->image_height;
+ image->display_w = vo->aspdat.prew;
+ image->display_h = vo->aspdat.preh;
+
+ mp_image_set_colorspace_details(image, &p->colorspace);
+
+ return image;
+}
+
+static int query_format(struct vo *vo, uint32_t format)
+{
+ struct gl_priv *p = vo->priv;
+
+ int depth;
+ int caps = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_FLIP |
+ VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | VFCAP_ACCEPT_STRIDE;
+ if (p->use_osd)
+ caps |= VFCAP_OSD;
+ if (format == IMGFMT_RGB24 || format == IMGFMT_RGBA)
+ return caps;
+ if (p->use_yuv && mp_get_chroma_shift(format, NULL, NULL, &depth) &&
+ (depth == 8 || depth == 16 || glYUVLargeRange(p->use_yuv)) &&
+ (IMGFMT_IS_YUVP16_NE(format) || !IMGFMT_IS_YUVP16(format)))
+ return caps;
+ // HACK, otherwise we get only b&w with some filters (e.g. -vf eq)
+ // ideally MPlayer should be fixed instead not to use Y800 when it has the choice
+ if (!p->use_yuv && (format == IMGFMT_Y8 || format == IMGFMT_Y800))
+ return 0;
+ if (!p->use_ycbcr && (format == IMGFMT_UYVY || format == IMGFMT_YVYU))
+ return 0;
+ if (p->many_fmts &&
+ glFindFormat(format, p->have_texture_rg, NULL, NULL, NULL, NULL))
+ return caps;
+ return 0;
+}
+
+static void uninit(struct vo *vo)
+{
+ struct gl_priv *p = vo->priv;
+
+ uninitGl(vo);
+ free(p->custom_prog);
+ p->custom_prog = NULL;
+ free(p->custom_tex);
+ p->custom_tex = NULL;
+ mpgl_uninit(p->glctx);
+ p->glctx = NULL;
+ p->gl = NULL;
+}
+
+static int backend_valid(void *arg)
+{
+ return mpgl_find_backend(*(const char **)arg) >= 0;
+}
+
+static int preinit(struct vo *vo, const char *arg)
+{
+ struct gl_priv *p = talloc_zero(vo, struct gl_priv);
+ vo->priv = p;
+
+ *p = (struct gl_priv) {
+ .many_fmts = 1,
+ .use_osd = -1,
+ .use_yuv = -1,
+ .colorspace = MP_CSP_DETAILS_DEFAULTS,
+ .filter_strength = 0.5,
+ .use_rectangle = -1,
+ .ati_hack = -1,
+ .force_pbo = -1,
+ .swap_interval = vo_vsync,
+ .custom_prog = NULL,
+ .custom_tex = NULL,
+ .custom_tlin = 1,
+ .osd_color = 0xffffff,
+ };
+
+ char *backend_arg = NULL;
+
+ //essentially unused; for legacy warnings only
+ int user_colorspace = 0;
+ int levelconv = -1;
+ int aspect = -1;
+
+ const opt_t subopts[] = {
+ {"manyfmts", OPT_ARG_BOOL, &p->many_fmts, NULL},
+ {"osd", OPT_ARG_BOOL, &p->use_osd, NULL},
+ {"scaled-osd", OPT_ARG_BOOL, &p->scaled_osd, NULL},
+ {"ycbcr", OPT_ARG_BOOL, &p->use_ycbcr, NULL},
+ {"slice-height", OPT_ARG_INT, &p->slice_height, int_non_neg},
+ {"rectangle", OPT_ARG_INT, &p->use_rectangle,int_non_neg},
+ {"yuv", OPT_ARG_INT, &p->use_yuv, int_non_neg},
+ {"lscale", OPT_ARG_INT, &p->lscale, int_non_neg},
+ {"cscale", OPT_ARG_INT, &p->cscale, int_non_neg},
+ {"filter-strength", OPT_ARG_FLOAT, &p->filter_strength, NULL},
+ {"noise-strength", OPT_ARG_FLOAT, &p->noise_strength, NULL},
+ {"ati-hack", OPT_ARG_BOOL, &p->ati_hack, NULL},
+ {"force-pbo", OPT_ARG_BOOL, &p->force_pbo, NULL},
+ {"glfinish", OPT_ARG_BOOL, &p->use_glFinish, NULL},
+ {"swapinterval", OPT_ARG_INT, &p->swap_interval,NULL},
+ {"customprog", OPT_ARG_MSTRZ,&p->custom_prog, NULL},
+ {"customtex", OPT_ARG_MSTRZ,&p->custom_tex, NULL},
+ {"customtlin", OPT_ARG_BOOL, &p->custom_tlin, NULL},
+ {"customtrect", OPT_ARG_BOOL, &p->custom_trect, NULL},
+ {"mipmapgen", OPT_ARG_BOOL, &p->mipmap_gen, NULL},
+ {"osdcolor", OPT_ARG_INT, &p->osd_color, NULL},
+ {"stereo", OPT_ARG_INT, &p->stereo_mode, NULL},
+ {"sw", OPT_ARG_BOOL, &p->allow_sw, NULL},
+ {"backend", OPT_ARG_MSTRZ,&backend_arg, backend_valid},
+ // Removed options.
+ // They are only parsed to notify the user about the replacements.
+ {"aspect", OPT_ARG_BOOL, &aspect, NULL},
+ {"colorspace", OPT_ARG_INT, &user_colorspace, NULL},
+ {"levelconv", OPT_ARG_INT, &levelconv, NULL},
+ {NULL}
+ };
+
+ if (subopt_parse(arg, subopts) != 0) {
+ mp_msg(MSGT_VO, MSGL_FATAL,
+ "\n-vo opengl_old command line help:\n"
+ "Example: mpv -vo opengl_old:slice-height=4\n"
+ "\nOptions:\n"
+ " nomanyfmts\n"
+ " Disable extended color formats for OpenGL 1.2 and later\n"
+ " slice-height=<0-...>\n"
+ " Slice size for texture transfer, 0 for whole image\n"
+ " noosd\n"
+ " Do not use OpenGL OSD code\n"
+ " scaled-osd\n"
+ " Render OSD at movie resolution and scale it\n"
+ " rectangle=<0,1,2>\n"
+ " 0: use power-of-two textures\n"
+ " 1: use texture_rectangle\n"
+ " 2: use texture_non_power_of_two\n"
+ " ati-hack\n"
+ " Workaround ATI bug with PBOs\n"
+ " force-pbo\n"
+ " Force use of PBO even if this involves an extra memcpy\n"
+ " glfinish\n"
+ " Call glFinish() before swapping buffers\n"
+ " swapinterval=<n>\n"
+ " Interval in displayed frames between to buffer swaps.\n"
+ " 1 is equivalent to enable VSYNC, 0 to disable VSYNC.\n"
+ " Requires GLX_SGI_swap_control support to work.\n"
+ " ycbcr\n"
+ " also try to use the GL_MESA_ycbcr_texture extension\n"
+ " yuv=<n>\n"
+ " 0: use software YUV to RGB conversion.\n"
+ " 1: deprecated, will use yuv=2 (used to be nVidia register combiners).\n"
+ " 2: use fragment program.\n"
+ " 3: use fragment program with gamma correction.\n"
+ " 4: use fragment program with gamma correction via lookup.\n"
+ " 5: use ATI-specific method (for older cards).\n"
+ " 6: use lookup via 3D texture.\n"
+ " lscale=<n>\n"
+ " 0: use standard bilinear scaling for luma.\n"
+ " 1: use improved bicubic scaling for luma.\n"
+ " 2: use cubic in X, linear in Y direction scaling for luma.\n"
+ " 3: as 1 but without using a lookup texture.\n"
+ " 4: experimental unsharp masking (sharpening).\n"
+ " 5: experimental unsharp masking (sharpening) with larger radius.\n"
+ " cscale=<n>\n"
+ " as lscale but for chroma (2x slower with little visible effect).\n"
+ " filter-strength=<value>\n"
+ " set the effect strength for some lscale/cscale filters\n"
+ " noise-strength=<value>\n"
+ " set how much noise to add. 1.0 is suitable for dithering to 6 bit.\n"
+ " customprog=<filename>\n"
+ " use a custom YUV conversion program\n"
+ " customtex=<filename>\n"
+ " use a custom YUV conversion lookup texture\n"
+ " nocustomtlin\n"
+ " use GL_NEAREST scaling for customtex texture\n"
+ " customtrect\n"
+ " use texture_rectangle for customtex texture\n"
+ " mipmapgen\n"
+ " generate mipmaps for the video image (use with TXB in customprog)\n"
+ " osdcolor=<0xAARRGGBB>\n"
+ " use the given color for the OSD\n"
+ " stereo=<n>\n"
+ " 0: normal display\n"
+ " 1: side-by-side to red-cyan stereo\n"
+ " 2: side-by-side to green-magenta stereo\n"
+ " 3: side-by-side to quadbuffer stereo\n"
+ " sw\n"
+ " allow using a software renderer, if such is detected\n"
+ " backend=<sys>\n"
+ " auto: auto-select (default)\n"
+ " cocoa: Cocoa/OSX\n"
+ " win: Win32/WGL\n"
+ " x11: X11/GLX\n"
+ "\n");
+ return -1;
+ }
+ if (user_colorspace != 0 || levelconv != -1) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] \"colorspace\" and \"levelconv\" "
+ "suboptions have been removed. Use options --colormatrix and"
+ " --colormatrix-input-range/--colormatrix-output-range instead.\n");
+ return -1;
+ }
+ if (aspect != -1) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] \"noaspect\" suboption has been "
+ "removed. Use --noaspect instead.\n");
+ return -1;
+ }
+ if (p->use_yuv == 1) {
+ mp_msg(MSGT_VO, MSGL_WARN, "[gl] yuv=1 (nVidia register combiners) have"
+ " been removed, using yuv=2 instead.\n");
+ p->use_yuv = 2;
+ }
+
+ int backend = backend_arg ? mpgl_find_backend(backend_arg) : GLTYPE_AUTO;
+ free(backend_arg);
+
+ p->glctx = mpgl_init(backend, vo);
+ if (!p->glctx)
+ goto err_out;
+ p->gl = p->glctx->gl;
+
+ if (p->use_yuv == -1) {
+ if (!create_window(vo, 320, 200, VOFLAG_HIDDEN))
+ goto err_out;
+ autodetectGlExtensions(vo);
+ // We created a window to test whether the GL context supports hardware
+ // acceleration and so on. Destroy that window to make sure all state
+ // associated with it is lost.
+ uninitGl(vo);
+ if (!mpgl_destroy_window(p->glctx))
+ goto err_out;
+ }
+ if (p->many_fmts)
+ mp_msg(MSGT_VO, MSGL_INFO, "[gl] using extended formats. "
+ "Use -vo gl:nomanyfmts if playback fails.\n");
+ mp_msg(MSGT_VO, MSGL_V, "[gl] Using %d as slice height "
+ "(0 means image height).\n", p->slice_height);
+
+ return 0;
+
+err_out:
+ uninit(vo);
+ return -1;
+}
+
+static int control(struct vo *vo, uint32_t request, void *data)
+{
+ struct gl_priv *p = vo->priv;
+
+ switch (request) {
+ case VOCTRL_QUERY_FORMAT:
+ return query_format(vo, *(uint32_t *)data);
+ case VOCTRL_DRAW_IMAGE:
+ return draw_image(vo, data);
+ case VOCTRL_ONTOP:
+ if (!p->glctx->ontop)
+ break;
+ p->glctx->ontop(vo);
+ return VO_TRUE;
+ case VOCTRL_FULLSCREEN:
+ p->glctx->fullscreen(vo);
+ resize(vo, vo->dwidth, vo->dheight);
+ return VO_TRUE;
+ case VOCTRL_BORDER:
+ if (!p->glctx->border)
+ break;
+ p->glctx->border(vo);
+ resize(vo, vo->dwidth, vo->dheight);
+ return VO_TRUE;
+ case VOCTRL_GET_PANSCAN:
+ return VO_TRUE;
+ case VOCTRL_SET_PANSCAN:
+ resize(vo, vo->dwidth, vo->dheight);
+ return VO_TRUE;
+ case VOCTRL_GET_EQUALIZER:
+ if (p->is_yuv) {
+ struct voctrl_get_equalizer_args *args = data;
+ return mp_csp_equalizer_get(&p->video_eq, args->name, args->valueptr)
+ >= 0 ? VO_TRUE : VO_NOTIMPL;
+ }
+ break;
+ case VOCTRL_SET_EQUALIZER:
+ if (p->is_yuv) {
+ struct voctrl_set_equalizer_args *args = data;
+ if (mp_csp_equalizer_set(&p->video_eq, args->name, args->value) < 0)
+ return VO_NOTIMPL;
+ update_yuvconv(vo);
+ vo->want_redraw = true;
+ return VO_TRUE;
+ }
+ break;
+ case VOCTRL_SET_YUV_COLORSPACE: {
+ bool supports_csp = (1 << p->use_yuv) & MASK_NOT_COMBINERS;
+ if (vo->config_count && supports_csp) {
+ p->colorspace = *(struct mp_csp_details *)data;
+ update_yuvconv(vo);
+ vo->want_redraw = true;
+ }
+ return VO_TRUE;
+ }
+ case VOCTRL_GET_YUV_COLORSPACE:
+ *(struct mp_csp_details *)data = p->colorspace;
+ return VO_TRUE;
+ case VOCTRL_UPDATE_SCREENINFO:
+ if (!p->glctx->update_xinerama_info)
+ break;
+ p->glctx->update_xinerama_info(vo);
+ return VO_TRUE;
+ case VOCTRL_REDRAW_FRAME:
+ do_render(vo);
+ return true;
+ case VOCTRL_PAUSE:
+ if (!p->glctx->pause)
+ break;
+ p->glctx->pause(vo);
+ return VO_TRUE;
+ case VOCTRL_RESUME:
+ if (!p->glctx->resume)
+ break;
+ p->glctx->resume(vo);
+ return VO_TRUE;
+ case VOCTRL_SCREENSHOT: {
+ struct voctrl_screenshot_args *args = data;
+ if (args->full_window)
+ args->out_image = glGetWindowScreenshot(p->gl);
+ else
+ args->out_image = get_screenshot(vo);
+ return true;
+ }
+ }
+ return VO_NOTIMPL;
+}
+
+const struct vo_driver video_out_opengl_old = {
+ .is_new = true,
+ .info = &(const vo_info_t) {
+ "OpenGL",
+ "opengl-old",
+ "Reimar Doeffinger <Reimar.Doeffinger@gmx.de>",
+ ""
+ },
+ .preinit = preinit,
+ .config = config,
+ .control = control,
+ .draw_slice = draw_slice,
+ .draw_osd = draw_osd,
+ .flip_page = flip_page,
+ .check_events = check_events,
+ .uninit = uninit,
+};
diff --git a/video/out/vo_opengl_shaders.glsl b/video/out/vo_opengl_shaders.glsl
new file mode 100644
index 0000000000..1f302889e4
--- /dev/null
+++ b/video/out/vo_opengl_shaders.glsl
@@ -0,0 +1,355 @@
+/*
+ * 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.
+ */
+
+// Note that this file is not directly passed as shader, but run through some
+// text processing functions, and in fact contains multiple vertex and fragment
+// shaders.
+
+// inserted at the beginning of all shaders
+#!section prelude
+
+// GLSL 1.20 compatibility layer
+// texture() should be assumed to always map to texture2D()
+#if __VERSION__ >= 130
+# define texture1D texture
+# define texture3D texture
+# define DECLARE_FRAGPARMS \
+ out vec4 out_color;
+#else
+# define texture texture2D
+# define DECLARE_FRAGPARMS
+# define out_color gl_FragColor
+# define in varying
+#endif
+
+// Earlier GLSL doesn't support mix() with bvec
+#if __VERSION__ >= 130
+vec3 srgb_compand(vec3 v)
+{
+ return mix(1.055 * pow(v, vec3(1.0/2.4)) - vec3(0.055), v * 12.92,
+ lessThanEqual(v, vec3(0.0031308)));
+}
+#endif
+
+#!section vertex_all
+
+#if __VERSION__ < 130
+# undef in
+# define in attribute
+# define out varying
+#endif
+
+uniform mat3 transform;
+uniform sampler3D lut_3d;
+
+in vec2 vertex_position;
+in vec4 vertex_color;
+out vec4 color;
+in vec2 vertex_texcoord;
+out vec2 texcoord;
+
+void main() {
+ vec3 position = vec3(vertex_position, 1);
+#ifndef FIXED_SCALE
+ position = transform * position;
+#endif
+ gl_Position = vec4(position, 1);
+ color = vertex_color;
+
+#ifdef USE_OSD_LINEAR_CONV
+ // If no 3dlut is being used, we need to pull up to linear light for
+ // the sRGB function. *IF* 3dlut is used, we do not.
+ color.rgb = pow(color.rgb, vec3(2.2));
+#endif
+#ifdef USE_OSD_3DLUT
+ color = vec4(texture3D(lut_3d, color.rgb).rgb, color.a);
+#endif
+#ifdef USE_OSD_SRGB
+ color.rgb = srgb_compand(color.rgb);
+#endif
+
+ texcoord = vertex_texcoord;
+}
+
+#!section frag_osd_libass
+uniform sampler2D textures[3];
+
+in vec2 texcoord;
+in vec4 color;
+DECLARE_FRAGPARMS
+
+void main() {
+ out_color = vec4(color.rgb, color.a * texture(textures[0], texcoord).r);
+}
+
+#!section frag_osd_rgba
+uniform sampler2D textures[3];
+
+in vec2 texcoord;
+DECLARE_FRAGPARMS
+
+void main() {
+ out_color = texture(textures[0], texcoord);
+}
+
+#!section frag_video
+uniform sampler2D textures[3];
+uniform vec2 textures_size[3];
+uniform sampler1D lut_c_1d;
+uniform sampler1D lut_l_1d;
+uniform sampler2D lut_c_2d;
+uniform sampler2D lut_l_2d;
+uniform sampler3D lut_3d;
+uniform sampler2D dither;
+uniform mat4x3 colormatrix;
+uniform vec3 inv_gamma;
+uniform float conv_gamma;
+uniform float dither_quantization;
+uniform float dither_multiply;
+uniform float filter_param1;
+uniform vec2 dither_size;
+
+in vec2 texcoord;
+DECLARE_FRAGPARMS
+
+vec4 sample_bilinear(sampler2D tex, vec2 texsize, vec2 texcoord) {
+ return texture(tex, texcoord);
+}
+
+// Explanation how bicubic scaling with only 4 texel fetches is done:
+// http://www.mate.tue.nl/mate/pdfs/10318.pdf
+// 'Efficient GPU-Based Texture Interpolation using Uniform B-Splines'
+// Explanation why this algorithm normally always blurs, even with unit scaling:
+// http://bigwww.epfl.ch/preprints/ruijters1001p.pdf
+// 'GPU Prefilter for Accurate Cubic B-spline Interpolation'
+vec4 calcweights(float s) {
+ vec4 t = vec4(-0.5, 0.1666, 0.3333, -0.3333) * s + vec4(1, 0, -0.5, 0.5);
+ t = t * s + vec4(0, 0, -0.5, 0.5);
+ t = t * s + vec4(-0.6666, 0, 0.8333, 0.1666);
+ vec2 a = vec2(1 / t.z, 1 / t.w);
+ t.xy = t.xy * a + vec2(1, 1);
+ t.x = t.x + s;
+ t.y = t.y - s;
+ return t;
+}
+
+vec4 sample_bicubic_fast(sampler2D tex, vec2 texsize, vec2 texcoord) {
+ vec2 pt = 1 / texsize;
+ vec2 fcoord = fract(texcoord * texsize + vec2(0.5, 0.5));
+ vec4 parmx = calcweights(fcoord.x);
+ vec4 parmy = calcweights(fcoord.y);
+ vec4 cdelta;
+ cdelta.xz = parmx.rg * vec2(-pt.x, pt.x);
+ cdelta.yw = parmy.rg * vec2(-pt.y, pt.y);
+ // first y-interpolation
+ vec4 ar = texture(tex, texcoord + cdelta.xy);
+ vec4 ag = texture(tex, texcoord + cdelta.xw);
+ vec4 ab = mix(ag, ar, parmy.b);
+ // second y-interpolation
+ vec4 br = texture(tex, texcoord + cdelta.zy);
+ vec4 bg = texture(tex, texcoord + cdelta.zw);
+ vec4 aa = mix(bg, br, parmy.b);
+ // x-interpolation
+ return mix(aa, ab, parmx.b);
+}
+
+float[2] weights2(sampler1D lookup, float f) {
+ vec4 c = texture1D(lookup, f);
+ return float[2](c.r, c.g);
+}
+
+float[4] weights4(sampler1D lookup, float f) {
+ vec4 c = texture1D(lookup, f);
+ return float[4](c.r, c.g, c.b, c.a);
+}
+
+float[6] weights6(sampler2D lookup, float f) {
+ vec4 c1 = texture(lookup, vec2(0.25, f));
+ vec4 c2 = texture(lookup, vec2(0.75, f));
+ return float[6](c1.r, c1.g, c1.b, c2.r, c2.g, c2.b);
+}
+
+float[8] weights8(sampler2D lookup, float f) {
+ vec4 c1 = texture(lookup, vec2(0.25, f));
+ vec4 c2 = texture(lookup, vec2(0.75, f));
+ return float[8](c1.r, c1.g, c1.b, c1.a, c2.r, c2.g, c2.b, c2.a);
+}
+
+float[12] weights12(sampler2D lookup, float f) {
+ vec4 c1 = texture(lookup, vec2(1.0/6.0, f));
+ vec4 c2 = texture(lookup, vec2(0.5, f));
+ vec4 c3 = texture(lookup, vec2(5.0/6.0, f));
+ return float[12](c1.r, c1.g, c1.b, c1.a,
+ c2.r, c2.g, c2.b, c2.a,
+ c3.r, c3.g, c3.b, c3.a);
+}
+
+float[16] weights16(sampler2D lookup, float f) {
+ vec4 c1 = texture(lookup, vec2(0.125, f));
+ vec4 c2 = texture(lookup, vec2(0.375, f));
+ vec4 c3 = texture(lookup, vec2(0.625, f));
+ vec4 c4 = texture(lookup, vec2(0.875, f));
+ return float[16](c1.r, c1.g, c1.b, c1.a, c2.r, c2.g, c2.b, c2.a,
+ c3.r, c3.g, c3.b, c3.a, c4.r, c4.g, c4.b, c4.a);
+}
+
+#define CONVOLUTION_SEP_N(NAME, N) \
+ vec4 NAME(sampler2D tex, vec2 texcoord, vec2 pt, float weights[N]) { \
+ vec4 res = vec4(0); \
+ for (int n = 0; n < N; n++) { \
+ res += weights[n] * texture(tex, texcoord + pt * n); \
+ } \
+ return res; \
+ }
+
+CONVOLUTION_SEP_N(convolution_sep2, 2)
+CONVOLUTION_SEP_N(convolution_sep4, 4)
+CONVOLUTION_SEP_N(convolution_sep6, 6)
+CONVOLUTION_SEP_N(convolution_sep8, 8)
+CONVOLUTION_SEP_N(convolution_sep12, 12)
+CONVOLUTION_SEP_N(convolution_sep16, 16)
+
+// The dir parameter is (0, 1) or (1, 0), and we expect the shader compiler to
+// remove all the redundant multiplications and additions.
+#define SAMPLE_CONVOLUTION_SEP_N(NAME, N, SAMPLERT, CONV_FUNC, WEIGHTS_FUNC)\
+ vec4 NAME(vec2 dir, SAMPLERT lookup, sampler2D tex, vec2 texsize, \
+ vec2 texcoord) { \
+ vec2 pt = (1 / texsize) * dir; \
+ float fcoord = dot(fract(texcoord * texsize - 0.5), dir); \
+ vec2 base = texcoord - fcoord * pt; \
+ return CONV_FUNC(tex, base - pt * (N / 2 - 1), pt, \
+ WEIGHTS_FUNC(lookup, fcoord)); \
+ }
+
+SAMPLE_CONVOLUTION_SEP_N(sample_convolution_sep2, 2, sampler1D, convolution_sep2, weights2)
+SAMPLE_CONVOLUTION_SEP_N(sample_convolution_sep4, 4, sampler1D, convolution_sep4, weights4)
+SAMPLE_CONVOLUTION_SEP_N(sample_convolution_sep6, 6, sampler2D, convolution_sep6, weights6)
+SAMPLE_CONVOLUTION_SEP_N(sample_convolution_sep8, 8, sampler2D, convolution_sep8, weights8)
+SAMPLE_CONVOLUTION_SEP_N(sample_convolution_sep12, 12, sampler2D, convolution_sep12, weights12)
+SAMPLE_CONVOLUTION_SEP_N(sample_convolution_sep16, 16, sampler2D, convolution_sep16, weights16)
+
+
+#define CONVOLUTION_N(NAME, N) \
+ vec4 NAME(sampler2D tex, vec2 texcoord, vec2 pt, float taps_x[N], \
+ float taps_y[N]) { \
+ vec4 res = vec4(0); \
+ for (int y = 0; y < N; y++) { \
+ vec4 line = vec4(0); \
+ for (int x = 0; x < N; x++) \
+ line += taps_x[x] * texture(tex, texcoord + pt * vec2(x, y));\
+ res += taps_y[y] * line; \
+ } \
+ return res; \
+ }
+
+CONVOLUTION_N(convolution2, 2)
+CONVOLUTION_N(convolution4, 4)
+CONVOLUTION_N(convolution6, 6)
+CONVOLUTION_N(convolution8, 8)
+CONVOLUTION_N(convolution12, 12)
+CONVOLUTION_N(convolution16, 16)
+
+#define SAMPLE_CONVOLUTION_N(NAME, N, SAMPLERT, CONV_FUNC, WEIGHTS_FUNC) \
+ vec4 NAME(SAMPLERT lookup, sampler2D tex, vec2 texsize, vec2 texcoord) {\
+ vec2 pt = 1 / texsize; \
+ vec2 fcoord = fract(texcoord * texsize - 0.5); \
+ vec2 base = texcoord - fcoord * pt; \
+ return CONV_FUNC(tex, base - pt * (N / 2 - 1), pt, \
+ WEIGHTS_FUNC(lookup, fcoord.x), \
+ WEIGHTS_FUNC(lookup, fcoord.y)); \
+ }
+
+SAMPLE_CONVOLUTION_N(sample_convolution2, 2, sampler1D, convolution2, weights2)
+SAMPLE_CONVOLUTION_N(sample_convolution4, 4, sampler1D, convolution4, weights4)
+SAMPLE_CONVOLUTION_N(sample_convolution6, 6, sampler2D, convolution6, weights6)
+SAMPLE_CONVOLUTION_N(sample_convolution8, 8, sampler2D, convolution8, weights8)
+SAMPLE_CONVOLUTION_N(sample_convolution12, 12, sampler2D, convolution12, weights12)
+SAMPLE_CONVOLUTION_N(sample_convolution16, 16, sampler2D, convolution16, weights16)
+
+
+// Unsharp masking
+vec4 sample_sharpen3(sampler2D tex, vec2 texsize, vec2 texcoord) {
+ vec2 pt = 1 / texsize;
+ vec2 st = pt * 0.5;
+ vec4 p = texture(tex, texcoord);
+ vec4 sum = texture(tex, texcoord + st * vec2(+1, +1))
+ + texture(tex, texcoord + st * vec2(+1, -1))
+ + texture(tex, texcoord + st * vec2(-1, +1))
+ + texture(tex, texcoord + st * vec2(-1, -1));
+ return p + (p - 0.25 * sum) * filter_param1;
+}
+
+vec4 sample_sharpen5(sampler2D tex, vec2 texsize, vec2 texcoord) {
+ vec2 pt = 1 / texsize;
+ vec2 st1 = pt * 1.2;
+ vec4 p = texture(tex, texcoord);
+ vec4 sum1 = texture(tex, texcoord + st1 * vec2(+1, +1))
+ + texture(tex, texcoord + st1 * vec2(+1, -1))
+ + texture(tex, texcoord + st1 * vec2(-1, +1))
+ + texture(tex, texcoord + st1 * vec2(-1, -1));
+ vec2 st2 = pt * 1.5;
+ vec4 sum2 = texture(tex, texcoord + st2 * vec2(+1, 0))
+ + texture(tex, texcoord + st2 * vec2( 0, +1))
+ + texture(tex, texcoord + st2 * vec2(-1, 0))
+ + texture(tex, texcoord + st2 * vec2( 0, -1));
+ vec4 t = p * 0.859375 + sum2 * -0.1171875 + sum1 * -0.09765625;
+ return p + t * filter_param1;
+}
+
+void main() {
+#ifdef USE_PLANAR
+ vec3 color = vec3(SAMPLE_L(textures[0], textures_size[0], texcoord).r,
+ SAMPLE_C(textures[1], textures_size[1], texcoord).r,
+ SAMPLE_C(textures[2], textures_size[2], texcoord).r);
+#else
+ vec3 color = SAMPLE_L(textures[0], textures_size[0], texcoord).rgb;
+#endif
+#ifdef USE_GBRP
+ color.gbr = color;
+#endif
+#ifdef USE_YGRAY
+ // NOTE: actually slightly wrong for 16 bit input video, and completely
+ // wrong for 9/10 bit input
+ color.gb = vec2(128.0/255.0);
+#endif
+#ifdef USE_COLORMATRIX
+ color = mat3(colormatrix) * color + colormatrix[3];
+#endif
+#ifdef USE_LINEAR_CONV
+ color = pow(color, vec3(2.2));
+#endif
+#ifdef USE_LINEAR_CONV_INV
+ // Convert from linear RGB to gamma RGB before putting it through the 3D-LUT
+ // in the final stage.
+ color = pow(color, vec3(1.0/2.2));
+#endif
+#ifdef USE_GAMMA_POW
+ color = pow(color, inv_gamma);
+#endif
+#ifdef USE_3DLUT
+ color = texture3D(lut_3d, color).rgb;
+#endif
+#ifdef USE_SRGB
+ color.rgb = srgb_compand(color.rgb);
+#endif
+#ifdef USE_DITHER
+ float dither_value = texture(dither, gl_FragCoord.xy / dither_size).r;
+ color = floor(color * dither_multiply + dither_value ) / dither_quantization;
+#endif
+ out_color = vec4(color, 1);
+}
diff --git a/video/out/vo_vdpau.c b/video/out/vo_vdpau.c
new file mode 100644
index 0000000000..a523ea5815
--- /dev/null
+++ b/video/out/vo_vdpau.c
@@ -0,0 +1,1718 @@
+/*
+ * VDPAU video output driver
+ *
+ * Copyright (C) 2008 NVIDIA
+ * Copyright (C) 2009 Uoti Urpala
+ *
+ * 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.
+ */
+
+/*
+ * Actual decoding and presentation are implemented here.
+ * All necessary frame information is collected through
+ * the "vdpau_render_state" structure after parsing all headers
+ * etc. in libavcodec for different codecs.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <limits.h>
+#include <assert.h>
+
+#include <libavutil/common.h>
+#include <libavcodec/vdpau.h>
+
+#include "config.h"
+#include "mp_msg.h"
+#include "options.h"
+#include "talloc.h"
+#include "video_out.h"
+#include "x11_common.h"
+#include "aspect.h"
+#include "csputils.h"
+#include "sub/sub.h"
+#include "m_option.h"
+#include "libmpcodecs/vfcap.h"
+#include "libmpcodecs/mp_image.h"
+#include "osdep/timer.h"
+#include "bitmap_packer.h"
+
+#define WRAP_ADD(x, a, m) ((a) < 0 \
+ ? ((x)+(a)+(m) < (m) ? (x)+(a)+(m) : (x)+(a)) \
+ : ((x)+(a) < (m) ? (x)+(a) : (x)+(a)-(m)))
+
+#define CHECK_ST_ERROR(message) \
+ do { \
+ if (vdp_st != VDP_STATUS_OK) { \
+ mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] %s: %s\n", \
+ message, vdp->get_error_string(vdp_st)); \
+ return -1; \
+ } \
+ } while (0)
+
+#define CHECK_ST_WARNING(message) \
+ do { \
+ if (vdp_st != VDP_STATUS_OK) \
+ mp_msg(MSGT_VO, MSGL_WARN, "[ vdpau] %s: %s\n", \
+ message, vdp->get_error_string(vdp_st)); \
+ } while (0)
+
+/* number of video and output surfaces */
+#define MAX_OUTPUT_SURFACES 15
+#define MAX_VIDEO_SURFACES 50
+#define NUM_BUFFERED_VIDEO 5
+
+/* Pixelformat used for output surfaces */
+#define OUTPUT_RGBA_FORMAT VDP_RGBA_FORMAT_B8G8R8A8
+
+/*
+ * Global variable declaration - VDPAU specific
+ */
+
+struct vdp_functions {
+#define VDP_FUNCTION(vdp_type, _, mp_name) vdp_type *mp_name;
+#include "vdpau_template.c"
+#undef VDP_FUNCTION
+};
+
+struct vdpctx {
+ struct vdp_functions *vdp;
+
+ VdpDevice vdp_device;
+ bool is_preempted;
+ bool preemption_acked;
+ bool preemption_user_notified;
+ unsigned int last_preemption_retry_fail;
+ VdpGetProcAddress *vdp_get_proc_address;
+
+ VdpPresentationQueueTarget flip_target;
+ VdpPresentationQueue flip_queue;
+ uint64_t last_vdp_time;
+ unsigned int last_sync_update;
+
+ VdpOutputSurface output_surfaces[MAX_OUTPUT_SURFACES];
+ VdpOutputSurface screenshot_surface;
+ int num_output_surfaces;
+ struct buffered_video_surface {
+ VdpVideoSurface surface;
+ double pts;
+ mp_image_t *mpi;
+ } buffered_video[NUM_BUFFERED_VIDEO];
+ int deint_queue_pos;
+ int output_surface_width, output_surface_height;
+
+ VdpVideoMixer video_mixer;
+ struct mp_csp_details colorspace;
+ int deint;
+ int deint_type;
+ int deint_counter;
+ int pullup;
+ float denoise;
+ float sharpen;
+ int hqscaling;
+ int chroma_deint;
+ int flip_offset_window;
+ int flip_offset_fs;
+ int top_field_first;
+ bool flip;
+
+ VdpDecoder decoder;
+ int decoder_max_refs;
+
+ VdpRect src_rect_vid;
+ VdpRect out_rect_vid;
+ struct mp_osd_res osd_rect;
+
+ struct vdpau_render_state surface_render[MAX_VIDEO_SURFACES];
+ int surface_num;
+ int query_surface_num;
+ VdpTime recent_vsync_time;
+ float user_fps;
+ int composite_detect;
+ unsigned int vsync_interval;
+ uint64_t last_queue_time;
+ uint64_t queue_time[MAX_OUTPUT_SURFACES];
+ uint64_t last_ideal_time;
+ bool dropped_frame;
+ uint64_t dropped_time;
+ uint32_t vid_width, vid_height;
+ uint32_t image_format;
+ VdpChromaType vdp_chroma_type;
+ VdpYCbCrFormat vdp_pixel_format;
+
+ // OSD
+ struct osd_bitmap_surface {
+ VdpRGBAFormat format;
+ VdpBitmapSurface surface;
+ uint32_t max_width;
+ uint32_t max_height;
+ struct bitmap_packer *packer;
+ // List of surfaces to be rendered
+ struct osd_target {
+ VdpRect source;
+ VdpRect dest;
+ VdpColor color;
+ } *targets;
+ int targets_size;
+ int render_count;
+ int bitmap_id;
+ int bitmap_pos_id;
+ } osd_surfaces[MAX_OSD_PARTS];
+
+ // Video equalizer
+ struct mp_csp_equalizer video_eq;
+
+ // These tell what's been initialized and uninit() should free/uninitialize
+ bool mode_switched;
+};
+
+static bool status_ok(struct vo *vo);
+
+static int change_vdptime_sync(struct vdpctx *vc, unsigned int *t)
+{
+ struct vdp_functions *vdp = vc->vdp;
+ VdpStatus vdp_st;
+ VdpTime vdp_time;
+ vdp_st = vdp->presentation_queue_get_time(vc->flip_queue, &vdp_time);
+ CHECK_ST_ERROR("Error when calling vdp_presentation_queue_get_time");
+ unsigned int t1 = *t;
+ unsigned int t2 = GetTimer();
+ uint64_t old = vc->last_vdp_time + (t1 - vc->last_sync_update) * 1000ULL;
+ if (vdp_time > old)
+ if (vdp_time > old + (t2 - t1) * 1000ULL)
+ vdp_time -= (t2 - t1) * 1000ULL;
+ else
+ vdp_time = old;
+ mp_msg(MSGT_VO, MSGL_DBG2, "[vdpau] adjusting VdpTime offset by %f µs\n",
+ (int64_t)(vdp_time - old) / 1000.);
+ vc->last_vdp_time = vdp_time;
+ vc->last_sync_update = t1;
+ *t = t2;
+ return 0;
+}
+
+static uint64_t sync_vdptime(struct vo *vo)
+{
+ struct vdpctx *vc = vo->priv;
+
+ unsigned int t = GetTimer();
+ if (t - vc->last_sync_update > 5000000)
+ change_vdptime_sync(vc, &t);
+ uint64_t now = (t - vc->last_sync_update) * 1000ULL + vc->last_vdp_time;
+ // Make sure nanosecond inaccuracies don't make things inconsistent
+ now = FFMAX(now, vc->recent_vsync_time);
+ return now;
+}
+
+static uint64_t convert_to_vdptime(struct vo *vo, unsigned int t)
+{
+ struct vdpctx *vc = vo->priv;
+ return (int)(t - vc->last_sync_update) * 1000LL + vc->last_vdp_time;
+}
+
+static int render_video_to_output_surface(struct vo *vo,
+ VdpOutputSurface output_surface,
+ VdpRect *output_rect)
+{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
+ VdpTime dummy;
+ VdpStatus vdp_st;
+ if (vc->deint_queue_pos < 0)
+ return -1;
+
+ struct buffered_video_surface *bv = vc->buffered_video;
+ int field = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
+ unsigned int dp = vc->deint_queue_pos;
+ // dp==0 means last field of latest frame, 1 earlier field of latest frame,
+ // 2 last field of previous frame and so on
+ if (vc->deint) {
+ field = vc->top_field_first ^ (dp & 1) ?
+ VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD:
+ VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD;
+ }
+ const VdpVideoSurface *past_fields = (const VdpVideoSurface []){
+ bv[(dp+1)/2].surface, bv[(dp+2)/2].surface};
+ const VdpVideoSurface *future_fields = (const VdpVideoSurface []){
+ dp >= 1 ? bv[(dp-1)/2].surface : VDP_INVALID_HANDLE};
+ vdp_st = vdp->presentation_queue_block_until_surface_idle(vc->flip_queue,
+ output_surface,
+ &dummy);
+ CHECK_ST_WARNING("Error when calling "
+ "vdp_presentation_queue_block_until_surface_idle");
+
+ vdp_st = vdp->video_mixer_render(vc->video_mixer, VDP_INVALID_HANDLE,
+ 0, field, 2, past_fields,
+ bv[dp/2].surface, 1, future_fields,
+ &vc->src_rect_vid, output_surface,
+ NULL, output_rect, 0, NULL);
+ CHECK_ST_WARNING("Error when calling vdp_video_mixer_render");
+ return 0;
+}
+
+static int video_to_output_surface(struct vo *vo)
+{
+ struct vdpctx *vc = vo->priv;
+
+ return render_video_to_output_surface(vo,
+ vc->output_surfaces[vc->surface_num],
+ &vc->out_rect_vid);
+}
+
+static int next_deint_queue_pos(struct vo *vo, bool eof)
+{
+ struct vdpctx *vc = vo->priv;
+
+ int dqp = vc->deint_queue_pos;
+ if (dqp < 0)
+ dqp += 1000;
+ else
+ dqp = vc->deint >= 2 ? dqp - 1 : dqp - 2 | 1;
+ if (dqp < (eof ? 0 : 3))
+ return -1;
+ return dqp;
+}
+
+static void set_next_frame_info(struct vo *vo, bool eof)
+{
+ struct vdpctx *vc = vo->priv;
+
+ vo->frame_loaded = false;
+ int dqp = next_deint_queue_pos(vo, eof);
+ if (dqp < 0)
+ return;
+ vo->frame_loaded = true;
+
+ // Set pts values
+ struct buffered_video_surface *bv = vc->buffered_video;
+ int idx = dqp >> 1;
+ if (idx == 0) { // no future frame/pts available
+ vo->next_pts = bv[0].pts;
+ vo->next_pts2 = MP_NOPTS_VALUE;
+ } else if (!(vc->deint >= 2)) { // no field-splitting deinterlace
+ vo->next_pts = bv[idx].pts;
+ vo->next_pts2 = bv[idx - 1].pts;
+ } else { // deinterlace with separate fields
+ double intermediate_pts;
+ double diff = bv[idx - 1].pts - bv[idx].pts;
+ if (diff > 0 && diff < 0.5)
+ intermediate_pts = (bv[idx].pts + bv[idx - 1].pts) / 2;
+ else
+ intermediate_pts = bv[idx].pts;
+ if (dqp & 1) { // first field
+ vo->next_pts = bv[idx].pts;
+ vo->next_pts2 = intermediate_pts;
+ } else {
+ vo->next_pts = intermediate_pts;
+ vo->next_pts2 = bv[idx - 1].pts;
+ }
+ }
+}
+
+static void add_new_video_surface(struct vo *vo, VdpVideoSurface surface,
+ struct mp_image *reserved_mpi, double pts)
+{
+ struct vdpctx *vc = vo->priv;
+ struct buffered_video_surface *bv = vc->buffered_video;
+
+ if (reserved_mpi)
+ reserved_mpi->usage_count++;
+ if (bv[NUM_BUFFERED_VIDEO - 1].mpi)
+ bv[NUM_BUFFERED_VIDEO - 1].mpi->usage_count--;
+
+ for (int i = NUM_BUFFERED_VIDEO - 1; i > 0; i--)
+ bv[i] = bv[i - 1];
+ bv[0] = (struct buffered_video_surface){
+ .mpi = reserved_mpi,
+ .surface = surface,
+ .pts = pts,
+ };
+
+ vc->deint_queue_pos = FFMIN(vc->deint_queue_pos + 2,
+ NUM_BUFFERED_VIDEO * 2 - 3);
+ set_next_frame_info(vo, false);
+}
+
+static void forget_frames(struct vo *vo)
+{
+ struct vdpctx *vc = vo->priv;
+
+ vc->deint_queue_pos = -1001;
+ vc->dropped_frame = false;
+ for (int i = 0; i < NUM_BUFFERED_VIDEO; i++) {
+ struct buffered_video_surface *p = vc->buffered_video + i;
+ if (p->mpi)
+ p->mpi->usage_count--;
+ *p = (struct buffered_video_surface){
+ .surface = VDP_INVALID_HANDLE,
+ };
+ }
+}
+
+static void resize(struct vo *vo)
+{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
+ VdpStatus vdp_st;
+ struct mp_rect src_rect;
+ struct mp_rect dst_rect;
+ vo_get_src_dst_rects(vo, &src_rect, &dst_rect, &vc->osd_rect);
+ vc->out_rect_vid.x0 = dst_rect.x0;
+ vc->out_rect_vid.x1 = dst_rect.x1;
+ vc->out_rect_vid.y0 = dst_rect.y0;
+ vc->out_rect_vid.y1 = dst_rect.y1;
+ vc->src_rect_vid.x0 = src_rect.x0;
+ vc->src_rect_vid.x1 = src_rect.x1;
+ vc->src_rect_vid.y0 = vc->flip ? src_rect.y1 : src_rect.y0;
+ vc->src_rect_vid.y1 = vc->flip ? src_rect.y0 : src_rect.y1;
+
+ int flip_offset_ms = vo_fs ? vc->flip_offset_fs : vc->flip_offset_window;
+ vo->flip_queue_offset = flip_offset_ms / 1000.;
+
+ if (vc->output_surface_width < vo->dwidth
+ || vc->output_surface_height < vo->dheight) {
+ if (vc->output_surface_width < vo->dwidth) {
+ vc->output_surface_width += vc->output_surface_width >> 1;
+ vc->output_surface_width = FFMAX(vc->output_surface_width,
+ vo->dwidth);
+ }
+ if (vc->output_surface_height < vo->dheight) {
+ vc->output_surface_height += vc->output_surface_height >> 1;
+ vc->output_surface_height = FFMAX(vc->output_surface_height,
+ vo->dheight);
+ }
+ // Creation of output_surfaces
+ for (int i = 0; i < vc->num_output_surfaces; i++)
+ if (vc->output_surfaces[i] != VDP_INVALID_HANDLE) {
+ vdp_st = vdp->output_surface_destroy(vc->output_surfaces[i]);
+ CHECK_ST_WARNING("Error when calling "
+ "vdp_output_surface_destroy");
+ }
+ for (int i = 0; i < vc->num_output_surfaces; i++) {
+ vdp_st = vdp->output_surface_create(vc->vdp_device,
+ OUTPUT_RGBA_FORMAT,
+ vc->output_surface_width,
+ vc->output_surface_height,
+ &vc->output_surfaces[i]);
+ CHECK_ST_WARNING("Error when calling vdp_output_surface_create");
+ mp_msg(MSGT_VO, MSGL_DBG2, "vdpau out create: %u\n",
+ vc->output_surfaces[i]);
+ }
+ }
+ vo->want_redraw = true;
+}
+
+static void preemption_callback(VdpDevice device, void *context)
+{
+ struct vdpctx *vc = context;
+ vc->is_preempted = true;
+ vc->preemption_acked = false;
+}
+
+/* Initialize vdp_get_proc_address, called from preinit() */
+static int win_x11_init_vdpau_procs(struct vo *vo)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ struct vdpctx *vc = vo->priv;
+ if (vc->vdp) // reinitialization after preemption
+ memset(vc->vdp, 0, sizeof(*vc->vdp));
+ else
+ vc->vdp = talloc_zero(vc, struct vdp_functions);
+ struct vdp_functions *vdp = vc->vdp;
+ VdpStatus vdp_st;
+
+ struct vdp_function {
+ const int id;
+ int offset;
+ };
+
+ const struct vdp_function *dsc;
+
+ static const struct vdp_function vdp_func[] = {
+#define VDP_FUNCTION(_, macro_name, mp_name) {macro_name, offsetof(struct vdp_functions, mp_name)},
+#include "vdpau_template.c"
+#undef VDP_FUNCTION
+ {0, -1}
+ };
+
+ vdp_st = vdp_device_create_x11(x11->display, x11->screen, &vc->vdp_device,
+ &vc->vdp_get_proc_address);
+ if (vdp_st != VDP_STATUS_OK) {
+ if (vc->is_preempted)
+ mp_msg(MSGT_VO, MSGL_DBG2, "[vdpau] Error calling "
+ "vdp_device_create_x11 while preempted: %d\n", vdp_st);
+ else
+ mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Error when calling "
+ "vdp_device_create_x11: %d\n", vdp_st);
+ return -1;
+ }
+
+ vdp->get_error_string = NULL;
+ for (dsc = vdp_func; dsc->offset >= 0; dsc++) {
+ vdp_st = vc->vdp_get_proc_address(vc->vdp_device, dsc->id,
+ (void **)((char *)vdp + dsc->offset));
+ if (vdp_st != VDP_STATUS_OK) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Error when calling "
+ "vdp_get_proc_address(function id %d): %s\n", dsc->id,
+ vdp->get_error_string ? vdp->get_error_string(vdp_st) : "?");
+ return -1;
+ }
+ }
+ vdp_st = vdp->preemption_callback_register(vc->vdp_device,
+ preemption_callback, vc);
+ return 0;
+}
+
+static int win_x11_init_vdpau_flip_queue(struct vo *vo)
+{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
+ struct vo_x11_state *x11 = vo->x11;
+ VdpStatus vdp_st;
+
+ if (vc->flip_target == VDP_INVALID_HANDLE) {
+ vdp_st = vdp->presentation_queue_target_create_x11(vc->vdp_device,
+ x11->window,
+ &vc->flip_target);
+ CHECK_ST_ERROR("Error when calling "
+ "vdp_presentation_queue_target_create_x11");
+ }
+
+ /* Emperically this seems to be the first call which fails when we
+ * try to reinit after preemption while the user is still switched
+ * from X to a virtual terminal (creating the vdp_device initially
+ * succeeds, as does creating the flip_target above). This is
+ * probably not guaranteed behavior, but we'll assume it as a simple
+ * way to reduce warnings while trying to recover from preemption.
+ */
+ if (vc->flip_queue == VDP_INVALID_HANDLE) {
+ vdp_st = vdp->presentation_queue_create(vc->vdp_device, vc->flip_target,
+ &vc->flip_queue);
+ if (vc->is_preempted && vdp_st != VDP_STATUS_OK) {
+ mp_msg(MSGT_VO, MSGL_DBG2, "[vdpau] Failed to create flip queue "
+ "while preempted: %s\n", vdp->get_error_string(vdp_st));
+ return -1;
+ } else
+ CHECK_ST_ERROR("Error when calling vdp_presentation_queue_create");
+ }
+
+ VdpTime vdp_time;
+ vdp_st = vdp->presentation_queue_get_time(vc->flip_queue, &vdp_time);
+ CHECK_ST_ERROR("Error when calling vdp_presentation_queue_get_time");
+ vc->last_vdp_time = vdp_time;
+ vc->last_sync_update = GetTimer();
+
+ vc->vsync_interval = 1;
+ if (vc->composite_detect && vo_x11_screen_is_composited(vo)) {
+ mp_msg(MSGT_VO, MSGL_INFO, "[vdpau] Compositing window manager "
+ "detected. Assuming timing info is inaccurate.\n");
+ } else if (vc->user_fps > 0) {
+ vc->vsync_interval = 1e9 / vc->user_fps;
+ mp_msg(MSGT_VO, MSGL_INFO, "[vdpau] Assuming user-specified display "
+ "refresh rate of %.3f Hz.\n", vc->user_fps);
+ } else if (vc->user_fps == 0) {
+#ifdef CONFIG_XF86VM
+ double fps = vo_vm_get_fps(vo);
+ if (!fps)
+ mp_msg(MSGT_VO, MSGL_WARN, "[vdpau] Failed to get display FPS\n");
+ else {
+ vc->vsync_interval = 1e9 / fps;
+ // This is verbose, but I'm not yet sure how common wrong values are
+ mp_msg(MSGT_VO, MSGL_INFO,
+ "[vdpau] Got display refresh rate %.3f Hz.\n"
+ "[vdpau] If that value looks wrong give the "
+ "-vo vdpau:fps=X suboption manually.\n", fps);
+ }
+#else
+ mp_msg(MSGT_VO, MSGL_INFO, "[vdpau] This binary has been compiled "
+ "without XF86VidMode support.\n");
+ mp_msg(MSGT_VO, MSGL_INFO, "[vdpau] Can't use vsync-aware timing "
+ "without manually provided -vo vdpau:fps=X suboption.\n");
+#endif
+ } else
+ mp_msg(MSGT_VO, MSGL_V, "[vdpau] framedrop/timing logic disabled by "
+ "user.\n");
+
+ return 0;
+}
+
+static int set_video_attribute(struct vdpctx *vc, VdpVideoMixerAttribute attr,
+ const void *value, char *attr_name)
+{
+ struct vdp_functions *vdp = vc->vdp;
+ VdpStatus vdp_st;
+
+ vdp_st = vdp->video_mixer_set_attribute_values(vc->video_mixer, 1, &attr,
+ &value);
+ if (vdp_st != VDP_STATUS_OK) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Error setting video mixer "
+ "attribute %s: %s\n", attr_name, vdp->get_error_string(vdp_st));
+ return -1;
+ }
+ return 0;
+}
+
+static void update_csc_matrix(struct vo *vo)
+{
+ struct vdpctx *vc = vo->priv;
+
+ mp_msg(MSGT_VO, MSGL_V, "[vdpau] Updating CSC matrix\n");
+
+ // VdpCSCMatrix happens to be compatible with mplayer's CSC matrix type
+ // both are float[3][4]
+ VdpCSCMatrix matrix;
+
+ struct mp_csp_params cparams = {
+ .colorspace = vc->colorspace, .input_bits = 8, .texture_bits = 8 };
+ mp_csp_copy_equalizer_values(&cparams, &vc->video_eq);
+ mp_get_yuv2rgb_coeffs(&cparams, matrix);
+
+ set_video_attribute(vc, VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX,
+ &matrix, "CSC matrix");
+}
+
+#define SET_VIDEO_ATTR(attr_name, attr_type, value) set_video_attribute(vc, \
+ VDP_VIDEO_MIXER_ATTRIBUTE_ ## attr_name, &(attr_type){value},\
+ # attr_name)
+static int create_vdp_mixer(struct vo *vo, VdpChromaType vdp_chroma_type)
+{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
+#define VDP_NUM_MIXER_PARAMETER 3
+#define MAX_NUM_FEATURES 6
+ int i;
+ VdpStatus vdp_st;
+
+ if (vc->video_mixer != VDP_INVALID_HANDLE)
+ return 0;
+
+ int feature_count = 0;
+ VdpVideoMixerFeature features[MAX_NUM_FEATURES];
+ VdpBool feature_enables[MAX_NUM_FEATURES];
+ static const VdpVideoMixerParameter parameters[VDP_NUM_MIXER_PARAMETER] = {
+ VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH,
+ VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT,
+ VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE,
+ };
+ const void *const parameter_values[VDP_NUM_MIXER_PARAMETER] = {
+ &vc->vid_width,
+ &vc->vid_height,
+ &vdp_chroma_type,
+ };
+ features[feature_count++] = VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL;
+ if (vc->deint_type == 4)
+ features[feature_count++] =
+ VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL;
+ if (vc->pullup)
+ features[feature_count++] = VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE;
+ if (vc->denoise)
+ features[feature_count++] = VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION;
+ if (vc->sharpen)
+ features[feature_count++] = VDP_VIDEO_MIXER_FEATURE_SHARPNESS;
+ if (vc->hqscaling) {
+ VdpVideoMixerFeature hqscaling_feature =
+ VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 + vc->hqscaling-1;
+ VdpBool hqscaling_available;
+ vdp_st = vdp->video_mixer_query_feature_support(vc->vdp_device,
+ hqscaling_feature,
+ &hqscaling_available);
+ CHECK_ST_ERROR("Error when calling video_mixer_query_feature_support");
+ if (hqscaling_available)
+ features[feature_count++] = hqscaling_feature;
+ else
+ mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Your hardware or VDPAU "
+ "library does not support requested hqscaling.\n");
+ }
+
+ vdp_st = vdp->video_mixer_create(vc->vdp_device, feature_count, features,
+ VDP_NUM_MIXER_PARAMETER,
+ parameters, parameter_values,
+ &vc->video_mixer);
+ CHECK_ST_ERROR("Error when calling vdp_video_mixer_create");
+
+ for (i = 0; i < feature_count; i++)
+ feature_enables[i] = VDP_TRUE;
+ if (vc->deint < 3)
+ feature_enables[0] = VDP_FALSE;
+ if (vc->deint_type == 4 && vc->deint < 4)
+ feature_enables[1] = VDP_FALSE;
+ if (feature_count) {
+ vdp_st = vdp->video_mixer_set_feature_enables(vc->video_mixer,
+ feature_count, features,
+ feature_enables);
+ CHECK_ST_WARNING("Error calling vdp_video_mixer_set_feature_enables");
+ }
+ if (vc->denoise)
+ SET_VIDEO_ATTR(NOISE_REDUCTION_LEVEL, float, vc->denoise);
+ if (vc->sharpen)
+ SET_VIDEO_ATTR(SHARPNESS_LEVEL, float, vc->sharpen);
+ if (!vc->chroma_deint)
+ SET_VIDEO_ATTR(SKIP_CHROMA_DEINTERLACE, uint8_t, 1);
+
+ update_csc_matrix(vo);
+ return 0;
+}
+
+// Free everything specific to a certain video file
+static void free_video_specific(struct vo *vo)
+{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
+ int i;
+ VdpStatus vdp_st;
+
+ if (vc->decoder != VDP_INVALID_HANDLE)
+ vdp->decoder_destroy(vc->decoder);
+ vc->decoder = VDP_INVALID_HANDLE;
+ vc->decoder_max_refs = -1;
+
+ forget_frames(vo);
+
+ for (i = 0; i < MAX_VIDEO_SURFACES; i++) {
+ if (vc->surface_render[i].surface != VDP_INVALID_HANDLE) {
+ vdp_st = vdp->video_surface_destroy(vc->surface_render[i].surface);
+ CHECK_ST_WARNING("Error when calling vdp_video_surface_destroy");
+ }
+ vc->surface_render[i].surface = VDP_INVALID_HANDLE;
+ }
+
+ if (vc->video_mixer != VDP_INVALID_HANDLE) {
+ vdp_st = vdp->video_mixer_destroy(vc->video_mixer);
+ CHECK_ST_WARNING("Error when calling vdp_video_mixer_destroy");
+ }
+ vc->video_mixer = VDP_INVALID_HANDLE;
+
+ if (vc->screenshot_surface != VDP_INVALID_HANDLE) {
+ vdp_st = vdp->output_surface_destroy(vc->screenshot_surface);
+ CHECK_ST_WARNING("Error when calling vdp_output_surface_destroy");
+ }
+ vc->screenshot_surface = VDP_INVALID_HANDLE;
+}
+
+static int create_vdp_decoder(struct vo *vo, int max_refs)
+{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
+ VdpStatus vdp_st;
+ VdpDecoderProfile vdp_decoder_profile;
+ if (vc->decoder != VDP_INVALID_HANDLE)
+ vdp->decoder_destroy(vc->decoder);
+ switch (vc->image_format) {
+ case IMGFMT_VDPAU_MPEG1:
+ vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG1;
+ break;
+ case IMGFMT_VDPAU_MPEG2:
+ vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG2_MAIN;
+ break;
+ case IMGFMT_VDPAU_H264:
+ vdp_decoder_profile = VDP_DECODER_PROFILE_H264_HIGH;
+ mp_msg(MSGT_VO, MSGL_V, "[vdpau] Creating H264 hardware decoder "
+ "for %d reference frames.\n", max_refs);
+ break;
+ case IMGFMT_VDPAU_WMV3:
+ vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_MAIN;
+ break;
+ case IMGFMT_VDPAU_VC1:
+ vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_ADVANCED;
+ break;
+ case IMGFMT_VDPAU_MPEG4:
+ vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG4_PART2_ASP;
+ break;
+ default:
+ mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Unknown image format!\n");
+ goto fail;
+ }
+ vdp_st = vdp->decoder_create(vc->vdp_device, vdp_decoder_profile,
+ vc->vid_width, vc->vid_height, max_refs,
+ &vc->decoder);
+ CHECK_ST_WARNING("Failed creating VDPAU decoder");
+ if (vdp_st != VDP_STATUS_OK) {
+ fail:
+ vc->decoder = VDP_INVALID_HANDLE;
+ vc->decoder_max_refs = 0;
+ return 0;
+ }
+ vc->decoder_max_refs = max_refs;
+ return 1;
+}
+
+static int initialize_vdpau_objects(struct vo *vo)
+{
+ struct vdpctx *vc = vo->priv;
+
+ vc->vdp_chroma_type = VDP_CHROMA_TYPE_420;
+ switch (vc->image_format) {
+ case IMGFMT_YV12:
+ case IMGFMT_I420:
+ case IMGFMT_IYUV:
+ vc->vdp_pixel_format = VDP_YCBCR_FORMAT_YV12;
+ break;
+ case IMGFMT_NV12:
+ vc->vdp_pixel_format = VDP_YCBCR_FORMAT_NV12;
+ break;
+ case IMGFMT_YUY2:
+ vc->vdp_pixel_format = VDP_YCBCR_FORMAT_YUYV;
+ vc->vdp_chroma_type = VDP_CHROMA_TYPE_422;
+ break;
+ case IMGFMT_UYVY:
+ vc->vdp_pixel_format = VDP_YCBCR_FORMAT_UYVY;
+ vc->vdp_chroma_type = VDP_CHROMA_TYPE_422;
+ }
+ if (win_x11_init_vdpau_flip_queue(vo) < 0)
+ return -1;
+
+ if (create_vdp_mixer(vo, vc->vdp_chroma_type) < 0)
+ return -1;
+
+ forget_frames(vo);
+ resize(vo);
+ return 0;
+}
+
+static void mark_vdpau_objects_uninitialized(struct vo *vo)
+{
+ struct vdpctx *vc = vo->priv;
+
+ vc->decoder = VDP_INVALID_HANDLE;
+ for (int i = 0; i < MAX_VIDEO_SURFACES; i++)
+ vc->surface_render[i].surface = VDP_INVALID_HANDLE;
+ forget_frames(vo);
+ vc->video_mixer = VDP_INVALID_HANDLE;
+ vc->flip_queue = VDP_INVALID_HANDLE;
+ vc->flip_target = VDP_INVALID_HANDLE;
+ for (int i = 0; i < MAX_OUTPUT_SURFACES; i++)
+ vc->output_surfaces[i] = VDP_INVALID_HANDLE;
+ vc->screenshot_surface = VDP_INVALID_HANDLE;
+ vc->vdp_device = VDP_INVALID_HANDLE;
+ for (int i = 0; i < MAX_OSD_PARTS; i++) {
+ struct osd_bitmap_surface *sfc = &vc->osd_surfaces[i];
+ talloc_free(sfc->packer);
+ sfc->bitmap_id = sfc->bitmap_pos_id = 0;
+ *sfc = (struct osd_bitmap_surface){
+ .surface = VDP_INVALID_HANDLE,
+ };
+ }
+ vc->output_surface_width = vc->output_surface_height = -1;
+}
+
+static int handle_preemption(struct vo *vo)
+{
+ struct vdpctx *vc = vo->priv;
+
+ if (!vc->is_preempted)
+ return 0;
+ if (!vc->preemption_acked)
+ mark_vdpau_objects_uninitialized(vo);
+ vc->preemption_acked = true;
+ if (!vc->preemption_user_notified) {
+ mp_tmsg(MSGT_VO, MSGL_ERR, "[vdpau] Got display preemption notice! "
+ "Will attempt to recover.\n");
+ vc->preemption_user_notified = true;
+ }
+ /* Trying to initialize seems to be quite slow, so only try once a
+ * second to avoid using 100% CPU. */
+ if (vc->last_preemption_retry_fail
+ && GetTimerMS() - vc->last_preemption_retry_fail < 1000)
+ return -1;
+ if (win_x11_init_vdpau_procs(vo) < 0 || initialize_vdpau_objects(vo) < 0) {
+ vc->last_preemption_retry_fail = GetTimerMS() | 1;
+ return -1;
+ }
+ vc->last_preemption_retry_fail = 0;
+ vc->is_preempted = false;
+ vc->preemption_user_notified = false;
+ mp_tmsg(MSGT_VO, MSGL_INFO, "[vdpau] Recovered from display preemption.\n");
+ return 1;
+}
+
+/*
+ * connect to X server, create and map window, initialize all
+ * VDPAU objects, create different surfaces etc.
+ */
+static int config(struct vo *vo, uint32_t width, uint32_t height,
+ uint32_t d_width, uint32_t d_height, uint32_t flags,
+ uint32_t format)
+{
+ struct vdpctx *vc = vo->priv;
+ struct vo_x11_state *x11 = vo->x11;
+ XVisualInfo vinfo;
+ XSetWindowAttributes xswa;
+ XWindowAttributes attribs;
+ unsigned long xswamask;
+ int depth;
+
+#ifdef CONFIG_XF86VM
+ int vm = flags & VOFLAG_MODESWITCHING;
+#endif
+
+ if (handle_preemption(vo) < 0)
+ return -1;
+
+ vc->flip = flags & VOFLAG_FLIPPING;
+ vc->image_format = format;
+ vc->vid_width = width;
+ vc->vid_height = height;
+
+ free_video_specific(vo);
+ if (IMGFMT_IS_VDPAU(vc->image_format) && !create_vdp_decoder(vo, 2))
+ return -1;
+
+#ifdef CONFIG_XF86VM
+ if (vm) {
+ vo_vm_switch(vo);
+ vc->mode_switched = true;
+ }
+#endif
+ XGetWindowAttributes(x11->display, DefaultRootWindow(x11->display),
+ &attribs);
+ depth = attribs.depth;
+ if (depth != 15 && depth != 16 && depth != 24 && depth != 32)
+ depth = 24;
+ XMatchVisualInfo(x11->display, x11->screen, depth, TrueColor, &vinfo);
+
+ xswa.background_pixel = 0;
+ xswa.border_pixel = 0;
+ /* Do not use CWBackPixel: It leads to VDPAU errors after
+ * aspect ratio changes. */
+ xswamask = CWBorderPixel;
+
+ vo_x11_create_vo_window(vo, &vinfo, vo->dx, vo->dy, d_width, d_height,
+ flags, CopyFromParent, "vdpau");
+ XChangeWindowAttributes(x11->display, x11->window, xswamask, &xswa);
+
+#ifdef CONFIG_XF86VM
+ if (vm) {
+ /* Grab the mouse pointer in our window */
+ if (vo_grabpointer)
+ XGrabPointer(x11->display, x11->window, True, 0,
+ GrabModeAsync, GrabModeAsync,
+ x11->window, None, CurrentTime);
+ XSetInputFocus(x11->display, x11->window, RevertToNone, CurrentTime);
+ }
+#endif
+
+ if (initialize_vdpau_objects(vo) < 0)
+ return -1;
+
+ return 0;
+}
+
+static void check_events(struct vo *vo)
+{
+ if (handle_preemption(vo) < 0)
+ return;
+
+ int e = vo_x11_check_events(vo);
+
+ if (e & VO_EVENT_RESIZE)
+ resize(vo);
+ else if (e & VO_EVENT_EXPOSE) {
+ vo->want_redraw = true;
+ }
+}
+
+static struct bitmap_packer *make_packer(struct vo *vo, VdpRGBAFormat format)
+{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
+
+ struct bitmap_packer *packer = talloc_zero(vo, struct bitmap_packer);
+ uint32_t w_max = 0, h_max = 0;
+ VdpStatus vdp_st = vdp->
+ bitmap_surface_query_capabilities(vc->vdp_device, format,
+ &(VdpBool){0}, &w_max, &h_max);
+ CHECK_ST_WARNING("Query to get max OSD surface size failed");
+ packer->w_max = w_max;
+ packer->h_max = h_max;
+ return packer;
+}
+
+static void draw_osd_part(struct vo *vo, int index)
+{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
+ VdpStatus vdp_st;
+ struct osd_bitmap_surface *sfc = &vc->osd_surfaces[index];
+ VdpOutputSurface output_surface = vc->output_surfaces[vc->surface_num];
+ int i;
+
+ VdpOutputSurfaceRenderBlendState blend_state = {
+ .struct_version = VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION,
+ .blend_factor_source_color =
+ VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA,
+ .blend_factor_source_alpha =
+ VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE,
+ .blend_factor_destination_color =
+ VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
+ .blend_factor_destination_alpha =
+ VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA,
+ .blend_equation_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
+ .blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
+ };
+
+ VdpOutputSurfaceRenderBlendState blend_state_premultiplied = blend_state;
+ blend_state_premultiplied.blend_factor_source_color =
+ VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE;
+
+ for (i = 0; i < sfc->render_count; i++) {
+ VdpOutputSurfaceRenderBlendState *blend = &blend_state;
+ if (sfc->format == VDP_RGBA_FORMAT_B8G8R8A8)
+ blend = &blend_state_premultiplied;
+ vdp_st = vdp->
+ output_surface_render_bitmap_surface(output_surface,
+ &sfc->targets[i].dest,
+ sfc->surface,
+ &sfc->targets[i].source,
+ &sfc->targets[i].color,
+ blend,
+ VDP_OUTPUT_SURFACE_RENDER_ROTATE_0);
+ CHECK_ST_WARNING("OSD: Error when rendering");
+ }
+}
+
+static void generate_osd_part(struct vo *vo, struct sub_bitmaps *imgs)
+{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
+ VdpStatus vdp_st;
+ struct osd_bitmap_surface *sfc = &vc->osd_surfaces[imgs->render_index];
+ bool need_upload = false;
+
+ if (imgs->bitmap_pos_id == sfc->bitmap_pos_id)
+ return; // Nothing changed and we still have the old data
+
+ sfc->render_count = 0;
+
+ if (imgs->format == SUBBITMAP_EMPTY || imgs->num_parts == 0)
+ return;
+
+ if (imgs->bitmap_id == sfc->bitmap_id)
+ goto osd_skip_upload;
+
+ need_upload = true;
+ VdpRGBAFormat format;
+ int format_size;
+ switch (imgs->format) {
+ case SUBBITMAP_LIBASS:
+ format = VDP_RGBA_FORMAT_A8;
+ format_size = 1;
+ break;
+ case SUBBITMAP_RGBA:
+ format = VDP_RGBA_FORMAT_B8G8R8A8;
+ format_size = 4;
+ break;
+ default:
+ abort();
+ };
+ if (sfc->format != format) {
+ talloc_free(sfc->packer);
+ sfc->packer = NULL;
+ };
+ sfc->format = format;
+ if (!sfc->packer)
+ sfc->packer = make_packer(vo, format);
+ sfc->packer->padding = imgs->scaled; // assume 2x2 filter on scaling
+ int r = packer_pack_from_subbitmaps(sfc->packer, imgs);
+ if (r < 0) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] OSD bitmaps do not fit on "
+ "a surface with the maximum supported size\n");
+ return;
+ } else if (r == 1) {
+ if (sfc->surface != VDP_INVALID_HANDLE) {
+ vdp_st = vdp->bitmap_surface_destroy(sfc->surface);
+ CHECK_ST_WARNING("Error when calling vdp_bitmap_surface_destroy");
+ }
+ mp_msg(MSGT_VO, MSGL_V, "[vdpau] Allocating a %dx%d surface for "
+ "OSD bitmaps.\n", sfc->packer->w, sfc->packer->h);
+ vdp_st = vdp->bitmap_surface_create(vc->vdp_device, format,
+ sfc->packer->w, sfc->packer->h,
+ true, &sfc->surface);
+ if (vdp_st != VDP_STATUS_OK)
+ sfc->surface = VDP_INVALID_HANDLE;
+ CHECK_ST_WARNING("OSD: error when creating surface");
+ }
+ if (imgs->scaled) {
+ char zeros[sfc->packer->used_width * format_size];
+ memset(zeros, 0, sizeof(zeros));
+ vdp_st = vdp->bitmap_surface_put_bits_native(sfc->surface,
+ &(const void *){zeros}, &(uint32_t){0},
+ &(VdpRect){0, 0, sfc->packer->used_width,
+ sfc->packer->used_height});
+ }
+
+osd_skip_upload:
+ if (sfc->surface == VDP_INVALID_HANDLE)
+ return;
+ if (sfc->packer->count > sfc->targets_size) {
+ talloc_free(sfc->targets);
+ sfc->targets_size = sfc->packer->count;
+ sfc->targets = talloc_size(vc, sfc->targets_size
+ * sizeof(*sfc->targets));
+ }
+
+ for (int i = 0 ;i < sfc->packer->count; i++) {
+ struct sub_bitmap *b = &imgs->parts[i];
+ struct osd_target *target = sfc->targets + sfc->render_count;
+ int x = sfc->packer->result[i].x;
+ int y = sfc->packer->result[i].y;
+ target->source = (VdpRect){x, y, x + b->w, y + b->h};
+ target->dest = (VdpRect){b->x, b->y, b->x + b->dw, b->y + b->dh};
+ target->color = (VdpColor){1, 1, 1, 1};
+ if (imgs->format == SUBBITMAP_LIBASS) {
+ uint32_t color = b->libass.color;
+ target->color.alpha = 1.0 - ((color >> 0) & 0xff) / 255.0;
+ target->color.blue = ((color >> 8) & 0xff) / 255.0;
+ target->color.green = ((color >> 16) & 0xff) / 255.0;
+ target->color.red = ((color >> 24) & 0xff) / 255.0;
+ }
+ if (need_upload) {
+ vdp_st = vdp->
+ bitmap_surface_put_bits_native(sfc->surface,
+ &(const void *){b->bitmap},
+ &(uint32_t){b->stride},
+ &target->source);
+ CHECK_ST_WARNING("OSD: putbits failed");
+ }
+ sfc->render_count++;
+ }
+
+ sfc->bitmap_id = imgs->bitmap_id;
+ sfc->bitmap_pos_id = imgs->bitmap_pos_id;
+}
+
+static void draw_osd_cb(void *ctx, struct sub_bitmaps *imgs)
+{
+ struct vo *vo = ctx;
+ generate_osd_part(vo, imgs);
+ draw_osd_part(vo, imgs->render_index);
+}
+
+static void draw_osd(struct vo *vo, struct osd_state *osd)
+{
+ struct vdpctx *vc = vo->priv;
+
+ if (!status_ok(vo))
+ return;
+
+ static const bool formats[SUBBITMAP_COUNT] = {
+ [SUBBITMAP_LIBASS] = true,
+ [SUBBITMAP_RGBA] = true,
+ };
+
+ osd_draw(osd, vc->osd_rect, osd->vo_pts, 0, formats, draw_osd_cb, vo);
+}
+
+static int update_presentation_queue_status(struct vo *vo)
+{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
+ VdpStatus vdp_st;
+
+ while (vc->query_surface_num != vc->surface_num) {
+ VdpTime vtime;
+ VdpPresentationQueueStatus status;
+ VdpOutputSurface surface = vc->output_surfaces[vc->query_surface_num];
+ vdp_st = vdp->presentation_queue_query_surface_status(vc->flip_queue,
+ surface,
+ &status, &vtime);
+ CHECK_ST_WARNING("Error calling "
+ "presentation_queue_query_surface_status");
+ if (status == VDP_PRESENTATION_QUEUE_STATUS_QUEUED)
+ break;
+ if (vc->vsync_interval > 1) {
+ uint64_t qtime = vc->queue_time[vc->query_surface_num];
+ if (vtime < qtime + vc->vsync_interval / 2)
+ mp_msg(MSGT_VO, MSGL_V, "[vdpau] Frame shown too early\n");
+ if (vtime > qtime + vc->vsync_interval)
+ mp_msg(MSGT_VO, MSGL_V, "[vdpau] Frame shown late\n");
+ }
+ vc->query_surface_num = WRAP_ADD(vc->query_surface_num, 1,
+ vc->num_output_surfaces);
+ vc->recent_vsync_time = vtime;
+ }
+ int num_queued = WRAP_ADD(vc->surface_num, -vc->query_surface_num,
+ vc->num_output_surfaces);
+ mp_msg(MSGT_VO, MSGL_DBG3, "[vdpau] Queued surface count (before add): "
+ "%d\n", num_queued);
+ return num_queued;
+}
+
+static inline uint64_t prev_vs2(struct vdpctx *vc, uint64_t ts, int shift)
+{
+ uint64_t offset = ts - vc->recent_vsync_time;
+ // Fix negative values for 1<<shift vsyncs before vc->recent_vsync_time
+ offset += (uint64_t)vc->vsync_interval << shift;
+ offset %= vc->vsync_interval;
+ return ts - offset;
+}
+
+static void flip_page_timed(struct vo *vo, unsigned int pts_us, int duration)
+{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
+ VdpStatus vdp_st;
+ uint32_t vsync_interval = vc->vsync_interval;
+
+ if (handle_preemption(vo) < 0)
+ return;
+
+ if (duration > INT_MAX / 1000)
+ duration = -1;
+ else
+ duration *= 1000;
+
+ if (vc->vsync_interval == 1)
+ duration = -1; // Make sure drop logic is disabled
+
+ uint64_t now = sync_vdptime(vo);
+ uint64_t pts = pts_us ? convert_to_vdptime(vo, pts_us) : now;
+ uint64_t ideal_pts = pts;
+ uint64_t npts = duration >= 0 ? pts + duration : UINT64_MAX;
+
+#define PREV_VS2(ts, shift) prev_vs2(vc, ts, shift)
+ // Only gives accurate results for ts >= vc->recent_vsync_time
+#define PREV_VSYNC(ts) PREV_VS2(ts, 0)
+
+ /* We hope to be here at least one vsync before the frame should be shown.
+ * If we are running late then don't drop the frame unless there is
+ * already one queued for the next vsync; even if we _hope_ to show the
+ * next frame soon enough to mean this one should be dropped we might
+ * not make the target time in reality. Without this check we could drop
+ * every frame, freezing the display completely if video lags behind.
+ */
+ if (now > PREV_VSYNC(FFMAX(pts, vc->last_queue_time + vsync_interval)))
+ npts = UINT64_MAX;
+
+ /* Allow flipping a frame at a vsync if its presentation time is a
+ * bit after that vsync and the change makes the flip time delta
+ * from previous frame better match the target timestamp delta.
+ * This avoids instability with frame timestamps falling near vsyncs.
+ * For example if the frame timestamps were (with vsyncs at
+ * integer values) 0.01, 1.99, 4.01, 5.99, 8.01, ... then
+ * straightforward timing at next vsync would flip the frames at
+ * 1, 2, 5, 6, 9; this changes it to 1, 2, 4, 6, 8 and so on with
+ * regular 2-vsync intervals.
+ *
+ * Also allow moving the frame forward if it looks like we dropped
+ * the previous frame incorrectly (now that we know better after
+ * having final exact timestamp information for this frame) and
+ * there would unnecessarily be a vsync without a frame change.
+ */
+ uint64_t vsync = PREV_VSYNC(pts);
+ if (pts < vsync + vsync_interval / 4
+ && (vsync - PREV_VS2(vc->last_queue_time, 16)
+ > pts - vc->last_ideal_time + vsync_interval / 2
+ || vc->dropped_frame && vsync > vc->dropped_time))
+ pts -= vsync_interval / 2;
+
+ vc->dropped_frame = true; // changed at end if false
+ vc->dropped_time = ideal_pts;
+
+ pts = FFMAX(pts, vc->last_queue_time + vsync_interval);
+ pts = FFMAX(pts, now);
+ if (npts < PREV_VSYNC(pts) + vsync_interval)
+ return;
+
+ int num_flips = update_presentation_queue_status(vo);
+ vsync = vc->recent_vsync_time + num_flips * vc->vsync_interval;
+ now = sync_vdptime(vo);
+ pts = FFMAX(pts, now);
+ pts = FFMAX(pts, vsync + (vsync_interval >> 2));
+ vsync = PREV_VSYNC(pts);
+ if (npts < vsync + vsync_interval)
+ return;
+ pts = vsync + (vsync_interval >> 2);
+ vdp_st =
+ vdp->presentation_queue_display(vc->flip_queue,
+ vc->output_surfaces[vc->surface_num],
+ vo->dwidth, vo->dheight, pts);
+ CHECK_ST_WARNING("Error when calling vdp_presentation_queue_display");
+
+ vc->last_queue_time = pts;
+ vc->queue_time[vc->surface_num] = pts;
+ vc->last_ideal_time = ideal_pts;
+ vc->dropped_frame = false;
+ vc->surface_num = WRAP_ADD(vc->surface_num, 1, vc->num_output_surfaces);
+}
+
+static int draw_slice(struct vo *vo, uint8_t *image[], int stride[], int w,
+ int h, int x, int y)
+{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
+ VdpStatus vdp_st;
+
+ if (handle_preemption(vo) < 0)
+ return VO_TRUE;
+
+ struct vdpau_render_state *rndr = (struct vdpau_render_state *)image[0];
+ int max_refs = vc->image_format == IMGFMT_VDPAU_H264 ?
+ rndr->info.h264.num_ref_frames : 2;
+ if (!IMGFMT_IS_VDPAU(vc->image_format))
+ return VO_FALSE;
+ if ((vc->decoder == VDP_INVALID_HANDLE || vc->decoder_max_refs < max_refs)
+ && !create_vdp_decoder(vo, max_refs))
+ return VO_FALSE;
+
+ vdp_st = vdp->decoder_render(vc->decoder, rndr->surface,
+ (void *)&rndr->info,
+ rndr->bitstream_buffers_used,
+ rndr->bitstream_buffers);
+ CHECK_ST_WARNING("Failed VDPAU decoder rendering");
+ return VO_TRUE;
+}
+
+
+static struct vdpau_render_state *get_surface(struct vo *vo, int number)
+{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
+
+ if (number >= MAX_VIDEO_SURFACES)
+ return NULL;
+ if (vc->surface_render[number].surface == VDP_INVALID_HANDLE
+ && !vc->is_preempted) {
+ VdpStatus vdp_st;
+ vdp_st = vdp->video_surface_create(vc->vdp_device, vc->vdp_chroma_type,
+ vc->vid_width, vc->vid_height,
+ &vc->surface_render[number].surface);
+ CHECK_ST_WARNING("Error when calling vdp_video_surface_create");
+ }
+ mp_msg(MSGT_VO, MSGL_DBG3, "vdpau vid create: %u\n",
+ vc->surface_render[number].surface);
+ return &vc->surface_render[number];
+}
+
+static void draw_image(struct vo *vo, mp_image_t *mpi, double pts)
+{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
+ struct mp_image *reserved_mpi = NULL;
+ struct vdpau_render_state *rndr;
+
+ if (IMGFMT_IS_VDPAU(vc->image_format)) {
+ rndr = mpi->priv;
+ reserved_mpi = mpi;
+ } else if (!(mpi->flags & MP_IMGFLAG_DRAW_CALLBACK)) {
+ rndr = get_surface(vo, vc->deint_counter);
+ vc->deint_counter = WRAP_ADD(vc->deint_counter, 1, NUM_BUFFERED_VIDEO);
+ if (handle_preemption(vo) >= 0) {
+ VdpStatus vdp_st;
+ const void *destdata[3] = {mpi->planes[0], mpi->planes[2],
+ mpi->planes[1]};
+ if (vc->image_format == IMGFMT_NV12)
+ destdata[1] = destdata[2];
+ vdp_st = vdp->video_surface_put_bits_y_cb_cr(rndr->surface,
+ vc->vdp_pixel_format, destdata, mpi->stride);
+ CHECK_ST_WARNING("Error when calling "
+ "vdp_video_surface_put_bits_y_cb_cr");
+ }
+ } else
+ // We don't support slice callbacks so this shouldn't occur -
+ // I think the flags test above in pointless, but I'm adding
+ // this instead of removing it just in case.
+ abort();
+ if (mpi->fields & MP_IMGFIELD_ORDERED)
+ vc->top_field_first = !!(mpi->fields & MP_IMGFIELD_TOP_FIRST);
+ else
+ vc->top_field_first = 1;
+
+ add_new_video_surface(vo, rndr->surface, reserved_mpi, pts);
+
+ return;
+}
+
+// warning: the size and pixel format of surface must match that of the
+// surfaces in vc->output_surfaces
+static struct mp_image *read_output_surface(struct vdpctx *vc,
+ VdpOutputSurface surface,
+ int width, int height)
+{
+ VdpStatus vdp_st;
+ struct vdp_functions *vdp = vc->vdp;
+ struct mp_image *image = alloc_mpi(width, height, IMGFMT_BGR32);
+ image->colorspace = MP_CSP_RGB;
+ image->levels = vc->colorspace.levels_out; // hardcoded with conv. matrix
+
+ void *dst_planes[] = { image->planes[0] };
+ uint32_t dst_pitches[] = { image->stride[0] };
+ vdp_st = vdp->output_surface_get_bits_native(surface, NULL, dst_planes,
+ dst_pitches);
+ CHECK_ST_WARNING("Error when calling vdp_output_surface_get_bits_native");
+
+ return image;
+}
+
+static struct mp_image *get_screenshot(struct vo *vo)
+{
+ struct vdpctx *vc = vo->priv;
+ VdpStatus vdp_st;
+ struct vdp_functions *vdp = vc->vdp;
+
+ if (vc->screenshot_surface == VDP_INVALID_HANDLE) {
+ vdp_st = vdp->output_surface_create(vc->vdp_device,
+ OUTPUT_RGBA_FORMAT,
+ vc->vid_width, vc->vid_height,
+ &vc->screenshot_surface);
+ CHECK_ST_WARNING("Error when calling vdp_output_surface_create");
+ }
+
+ VdpRect rc = { .x1 = vc->vid_width, .y1 = vc->vid_height };
+ render_video_to_output_surface(vo, vc->screenshot_surface, &rc);
+
+ struct mp_image *image = read_output_surface(vc, vc->screenshot_surface,
+ vc->vid_width, vc->vid_height);
+
+ image->display_w = vo->aspdat.prew;
+ image->display_h = vo->aspdat.preh;
+
+ return image;
+}
+
+static struct mp_image *get_window_screenshot(struct vo *vo)
+{
+ struct vdpctx *vc = vo->priv;
+ int last_surface = WRAP_ADD(vc->surface_num, -1, vc->num_output_surfaces);
+ VdpOutputSurface screen = vc->output_surfaces[last_surface];
+ struct mp_image *image = read_output_surface(vo->priv, screen,
+ vc->output_surface_width,
+ vc->output_surface_height);
+ image->width = image->w = vo->dwidth;
+ image->height = image->h = vo->dheight;
+ return image;
+}
+
+static uint32_t get_image(struct vo *vo, mp_image_t *mpi)
+{
+ struct vdpctx *vc = vo->priv;
+ struct vdpau_render_state *rndr;
+
+ // no dr for non-decoding for now
+ if (!IMGFMT_IS_VDPAU(vc->image_format))
+ return VO_FALSE;
+ if (mpi->type != MP_IMGTYPE_NUMBERED)
+ return VO_FALSE;
+
+ rndr = get_surface(vo, mpi->number);
+ if (!rndr) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] no surfaces available in "
+ "get_image\n");
+ // TODO: this probably breaks things forever, provide a dummy buffer?
+ return VO_FALSE;
+ }
+ mpi->flags |= MP_IMGFLAG_DIRECT;
+ mpi->stride[0] = mpi->stride[1] = mpi->stride[2] = 0;
+ mpi->planes[0] = mpi->planes[1] = mpi->planes[2] = NULL;
+ // hack to get around a check and to avoid a special-case in vd_ffmpeg.c
+ mpi->planes[0] = (void *)rndr;
+ mpi->num_planes = 1;
+ mpi->priv = rndr;
+ return VO_TRUE;
+}
+
+static int query_format(uint32_t format)
+{
+ int default_flags = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW
+ | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | VFCAP_OSD
+ | VFCAP_FLIP;
+ switch (format) {
+ case IMGFMT_YV12:
+ case IMGFMT_I420:
+ case IMGFMT_IYUV:
+ case IMGFMT_NV12:
+ case IMGFMT_YUY2:
+ case IMGFMT_UYVY:
+ return default_flags | VOCAP_NOSLICES;
+ case IMGFMT_VDPAU_MPEG1:
+ case IMGFMT_VDPAU_MPEG2:
+ case IMGFMT_VDPAU_H264:
+ case IMGFMT_VDPAU_WMV3:
+ case IMGFMT_VDPAU_VC1:
+ case IMGFMT_VDPAU_MPEG4:
+ return default_flags;
+ }
+ return 0;
+}
+
+static void destroy_vdpau_objects(struct vo *vo)
+{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
+
+ int i;
+ VdpStatus vdp_st;
+
+ free_video_specific(vo);
+
+ if (vc->flip_queue != VDP_INVALID_HANDLE) {
+ vdp_st = vdp->presentation_queue_destroy(vc->flip_queue);
+ CHECK_ST_WARNING("Error when calling vdp_presentation_queue_destroy");
+ }
+
+ if (vc->flip_target != VDP_INVALID_HANDLE) {
+ vdp_st = vdp->presentation_queue_target_destroy(vc->flip_target);
+ CHECK_ST_WARNING("Error when calling "
+ "vdp_presentation_queue_target_destroy");
+ }
+
+ for (i = 0; i < vc->num_output_surfaces; i++) {
+ if (vc->output_surfaces[i] == VDP_INVALID_HANDLE)
+ continue;
+ vdp_st = vdp->output_surface_destroy(vc->output_surfaces[i]);
+ CHECK_ST_WARNING("Error when calling vdp_output_surface_destroy");
+ }
+
+ for (int i = 0; i < MAX_OSD_PARTS; i++) {
+ struct osd_bitmap_surface *sfc = &vc->osd_surfaces[i];
+ if (sfc->surface != VDP_INVALID_HANDLE) {
+ vdp_st = vdp->bitmap_surface_destroy(sfc->surface);
+ CHECK_ST_WARNING("Error when calling vdp_bitmap_surface_destroy");
+ }
+ }
+
+ vdp_st = vdp->device_destroy(vc->vdp_device);
+ CHECK_ST_WARNING("Error when calling vdp_device_destroy");
+}
+
+static void uninit(struct vo *vo)
+{
+ struct vdpctx *vc = vo->priv;
+
+ /* Destroy all vdpau objects */
+ destroy_vdpau_objects(vo);
+
+#ifdef CONFIG_XF86VM
+ if (vc->mode_switched)
+ vo_vm_close(vo);
+#endif
+ vo_x11_uninit(vo);
+
+ // Free bitstream buffers allocated by FFmpeg
+ for (int i = 0; i < MAX_VIDEO_SURFACES; i++)
+ av_freep(&vc->surface_render[i].bitstream_buffers);
+}
+
+static int preinit(struct vo *vo, const char *arg)
+{
+ struct vdpctx *vc = vo->priv;
+
+ // Mark everything as invalid first so uninit() can tell what has been
+ // allocated
+ mark_vdpau_objects_uninitialized(vo);
+
+ vc->colorspace = (struct mp_csp_details) MP_CSP_DETAILS_DEFAULTS;
+ vc->video_eq.capabilities = MP_CSP_EQ_CAPS_COLORMATRIX;
+
+ vc->deint_type = vc->deint ? FFABS(vc->deint) : 3;
+ if (vc->deint < 0)
+ vc->deint = 0;
+
+ if (!vo_init(vo))
+ return -1;
+
+ // After this calling uninit() should work to free resources
+
+ if (win_x11_init_vdpau_procs(vo) < 0) {
+ if (vc->vdp && vc->vdp->device_destroy)
+ vc->vdp->device_destroy(vc->vdp_device);
+ vo_x11_uninit(vo);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int get_equalizer(struct vo *vo, const char *name, int *value)
+{
+ struct vdpctx *vc = vo->priv;
+ return mp_csp_equalizer_get(&vc->video_eq, name, value) >= 0 ?
+ VO_TRUE : VO_NOTIMPL;
+}
+
+static bool status_ok(struct vo *vo)
+{
+ if (!vo->config_ok || handle_preemption(vo) < 0)
+ return false;
+ return true;
+}
+
+static int set_equalizer(struct vo *vo, const char *name, int value)
+{
+ struct vdpctx *vc = vo->priv;
+
+ if (mp_csp_equalizer_set(&vc->video_eq, name, value) < 0)
+ return VO_NOTIMPL;
+
+ if (status_ok(vo))
+ update_csc_matrix(vo);
+ return true;
+}
+
+static void checked_resize(struct vo *vo)
+{
+ if (!status_ok(vo))
+ return;
+ resize(vo);
+}
+
+static int control(struct vo *vo, uint32_t request, void *data)
+{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
+
+ handle_preemption(vo);
+
+ switch (request) {
+ case VOCTRL_GET_DEINTERLACE:
+ *(int *)data = vc->deint;
+ return VO_TRUE;
+ case VOCTRL_SET_DEINTERLACE:
+ vc->deint = *(int *)data;
+ if (vc->deint)
+ vc->deint = vc->deint_type;
+ if (vc->deint_type > 2 && status_ok(vo)) {
+ VdpStatus vdp_st;
+ VdpVideoMixerFeature features[1] =
+ {vc->deint_type == 3 ?
+ VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL :
+ VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL};
+ VdpBool feature_enables[1] = {vc->deint ? VDP_TRUE : VDP_FALSE};
+ vdp_st = vdp->video_mixer_set_feature_enables(vc->video_mixer,
+ 1, features,
+ feature_enables);
+ CHECK_ST_WARNING("Error changing deinterlacing settings");
+ }
+ vo->want_redraw = true;
+ return VO_TRUE;
+ case VOCTRL_PAUSE:
+ if (vc->dropped_frame)
+ vo->want_redraw = true;
+ return true;
+ case VOCTRL_QUERY_FORMAT:
+ return query_format(*(uint32_t *)data);
+ case VOCTRL_GET_IMAGE:
+ return get_image(vo, data);
+ case VOCTRL_DRAW_IMAGE:
+ abort(); // draw_image() should get called directly
+ case VOCTRL_BORDER:
+ vo_x11_border(vo);
+ checked_resize(vo);
+ return VO_TRUE;
+ case VOCTRL_FULLSCREEN:
+ vo_x11_fullscreen(vo);
+ checked_resize(vo);
+ return VO_TRUE;
+ case VOCTRL_GET_PANSCAN:
+ return VO_TRUE;
+ case VOCTRL_SET_PANSCAN:
+ checked_resize(vo);
+ return VO_TRUE;
+ case VOCTRL_SET_EQUALIZER: {
+ vo->want_redraw = true;
+ struct voctrl_set_equalizer_args *args = data;
+ return set_equalizer(vo, args->name, args->value);
+ }
+ case VOCTRL_GET_EQUALIZER: {
+ struct voctrl_get_equalizer_args *args = data;
+ return get_equalizer(vo, args->name, args->valueptr);
+ }
+ case VOCTRL_SET_YUV_COLORSPACE:
+ vc->colorspace = *(struct mp_csp_details *)data;
+ if (status_ok(vo))
+ update_csc_matrix(vo);
+ vo->want_redraw = true;
+ return true;
+ case VOCTRL_GET_YUV_COLORSPACE:
+ *(struct mp_csp_details *)data = vc->colorspace;
+ return true;
+ case VOCTRL_ONTOP:
+ vo_x11_ontop(vo);
+ return VO_TRUE;
+ case VOCTRL_UPDATE_SCREENINFO:
+ update_xinerama_info(vo);
+ return VO_TRUE;
+ case VOCTRL_NEWFRAME:
+ vc->deint_queue_pos = next_deint_queue_pos(vo, true);
+ if (status_ok(vo))
+ video_to_output_surface(vo);
+ return true;
+ case VOCTRL_SKIPFRAME:
+ vc->deint_queue_pos = next_deint_queue_pos(vo, true);
+ return true;
+ case VOCTRL_REDRAW_FRAME:
+ if (status_ok(vo))
+ video_to_output_surface(vo);
+ return true;
+ case VOCTRL_RESET:
+ forget_frames(vo);
+ return true;
+ case VOCTRL_SCREENSHOT: {
+ if (!status_ok(vo))
+ return false;
+ struct voctrl_screenshot_args *args = data;
+ if (args->full_window)
+ args->out_image = get_window_screenshot(vo);
+ else
+ args->out_image = get_screenshot(vo);
+ return true;
+ }
+ }
+ return VO_NOTIMPL;
+}
+
+#undef OPT_BASE_STRUCT
+#define OPT_BASE_STRUCT struct vdpctx
+
+const struct vo_driver video_out_vdpau = {
+ .is_new = true,
+ .buffer_frames = true,
+ .info = &(const struct vo_info_s){
+ "VDPAU with X11",
+ "vdpau",
+ "Rajib Mahapatra <rmahapatra@nvidia.com> and others",
+ ""
+ },
+ .preinit = preinit,
+ .config = config,
+ .control = control,
+ .draw_image = draw_image,
+ .get_buffered_frame = set_next_frame_info,
+ .draw_slice = draw_slice,
+ .draw_osd = draw_osd,
+ .flip_page_timed = flip_page_timed,
+ .check_events = check_events,
+ .uninit = uninit,
+ .priv_size = sizeof(struct vdpctx),
+ .options = (const struct m_option []){
+ OPT_INTRANGE("deint", deint, 0, -4, 4),
+ OPT_FLAG_ON("chroma-deint", chroma_deint, 0, OPTDEF_INT(1)),
+ OPT_FLAG_OFF("nochroma-deint", chroma_deint, 0),
+ OPT_MAKE_FLAGS("pullup", pullup, 0),
+ OPT_FLOATRANGE("denoise", denoise, 0, 0, 1),
+ OPT_FLOATRANGE("sharpen", sharpen, 0, -1, 1),
+ OPT_INTRANGE("hqscaling", hqscaling, 0, 0, 9),
+ OPT_FLOAT("fps", user_fps, 0),
+ OPT_FLAG_ON("composite-detect", composite_detect, 0, OPTDEF_INT(1)),
+ OPT_INT("queuetime_windowed", flip_offset_window, 0, OPTDEF_INT(50)),
+ OPT_INT("queuetime_fs", flip_offset_fs, 0, OPTDEF_INT(50)),
+ OPT_INTRANGE("output_surfaces", num_output_surfaces, 0,
+ 2, MAX_OUTPUT_SURFACES, OPTDEF_INT(3)),
+ {NULL},
+ }
+};
diff --git a/video/out/vo_x11.c b/video/out/vo_x11.c
new file mode 100644
index 0000000000..2358b0a295
--- /dev/null
+++ b/video/out/vo_x11.c
@@ -0,0 +1,620 @@
+/*
+ * 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 <sys/types.h>
+
+#include "config.h"
+#include "video_out.h"
+#include "aspect.h"
+#include "csputils.h"
+#include "libmpcodecs/mp_image.h"
+#include "libmpcodecs/vfcap.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include <errno.h>
+
+#include "x11_common.h"
+
+#ifdef HAVE_SHM
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <X11/extensions/XShm.h>
+#endif
+
+#include "sub/sub.h"
+
+#include "libmpcodecs/sws_utils.h"
+#define MODE_RGB 0x1
+#define MODE_BGR 0x2
+
+#include "mp_msg.h"
+
+extern int sws_flags;
+
+struct priv {
+ struct vo *vo;
+
+ /* local data */
+ unsigned char *ImageData;
+ //! original unaligned pointer for free
+ unsigned char *ImageDataOrig;
+
+ /* X11 related variables */
+ XImage *myximage;
+ int depth, bpp;
+ XWindowAttributes attribs;
+
+ int int_pause;
+
+ int Flip_Flag;
+ int zoomFlag;
+
+ uint32_t image_width;
+ uint32_t image_height;
+ uint32_t in_format;
+ uint32_t out_format;
+ int out_offset;
+ int srcW;
+ int srcH;
+
+ int old_vo_dwidth;
+ int old_vo_dheight;
+
+ struct SwsContext *swsContext;
+ int dst_width;
+
+ XVisualInfo vinfo;
+
+ int firstTime;
+
+#ifdef HAVE_SHM
+ int Shmem_Flag;
+
+ XShmSegmentInfo Shminfo[1];
+ int gXErrorFlag;
+ int CompletionType;
+#endif
+};
+
+static void flip_page(struct vo *vo);
+
+static void check_events(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+
+ int ret = vo_x11_check_events(vo);
+
+ if (ret & VO_EVENT_RESIZE)
+ vo_x11_clearwindow(vo, vo->x11->window);
+ else if (ret & VO_EVENT_EXPOSE)
+ vo_x11_clearwindow_part(vo, vo->x11->window, p->myximage->width,
+ p->myximage->height);
+ if (ret & VO_EVENT_EXPOSE && p->int_pause)
+ flip_page(vo);
+}
+
+static void getMyXImage(struct priv *p)
+{
+ struct vo *vo = p->vo;
+#ifdef HAVE_SHM
+ if (vo->x11->display_is_local && XShmQueryExtension(vo->x11->display))
+ p->Shmem_Flag = 1;
+ else {
+ p->Shmem_Flag = 0;
+ mp_msg(MSGT_VO, MSGL_WARN,
+ "Shared memory not supported\nReverting to normal Xlib\n");
+ }
+ if (p->Shmem_Flag)
+ p->CompletionType = XShmGetEventBase(vo->x11->display) + ShmCompletion;
+
+ if (p->Shmem_Flag) {
+ p->myximage =
+ XShmCreateImage(vo->x11->display, p->vinfo.visual, p->depth,
+ ZPixmap, NULL, &p->Shminfo[0], p->image_width,
+ p->image_height);
+ if (p->myximage == NULL) {
+ mp_msg(MSGT_VO, MSGL_WARN,
+ "Shared memory error,disabling ( Ximage error )\n");
+ goto shmemerror;
+ }
+ p->Shminfo[0].shmid = shmget(IPC_PRIVATE,
+ p->myximage->bytes_per_line *
+ p->myximage->height,
+ IPC_CREAT | 0777);
+ if (p->Shminfo[0].shmid < 0) {
+ XDestroyImage(p->myximage);
+ mp_msg(MSGT_VO, MSGL_V, "%s\n", strerror(errno));
+ //perror( strerror( errno ) );
+ mp_msg(MSGT_VO, MSGL_WARN,
+ "Shared memory error,disabling ( seg id error )\n");
+ goto shmemerror;
+ }
+ p->Shminfo[0].shmaddr = (char *) shmat(p->Shminfo[0].shmid, 0, 0);
+
+ if (p->Shminfo[0].shmaddr == ((char *) -1)) {
+ XDestroyImage(p->myximage);
+ if (p->Shminfo[0].shmaddr != ((char *) -1))
+ shmdt(p->Shminfo[0].shmaddr);
+ mp_msg(MSGT_VO, MSGL_WARN,
+ "Shared memory error,disabling ( address error )\n");
+ goto shmemerror;
+ }
+ p->myximage->data = p->Shminfo[0].shmaddr;
+ p->ImageData = (unsigned char *) p->myximage->data;
+ p->Shminfo[0].readOnly = False;
+ XShmAttach(vo->x11->display, &p->Shminfo[0]);
+
+ XSync(vo->x11->display, False);
+
+ if (p->gXErrorFlag) {
+ XDestroyImage(p->myximage);
+ shmdt(p->Shminfo[0].shmaddr);
+ mp_msg(MSGT_VO, MSGL_WARN, "Shared memory error,disabling.\n");
+ p->gXErrorFlag = 0;
+ goto shmemerror;
+ } else
+ shmctl(p->Shminfo[0].shmid, IPC_RMID, 0);
+
+ if (!p->firstTime) {
+ mp_msg(MSGT_VO, MSGL_V, "Sharing memory.\n");
+ p->firstTime = 1;
+ }
+ } else {
+shmemerror:
+ p->Shmem_Flag = 0;
+#endif
+ p->myximage =
+ XCreateImage(vo->x11->display, p->vinfo.visual, p->depth, ZPixmap,
+ 0, NULL, p->image_width, p->image_height, 8, 0);
+ p->ImageDataOrig =
+ malloc(p->myximage->bytes_per_line * p->image_height + 32);
+ p->myximage->data = p->ImageDataOrig + 16 - ((long)p->ImageDataOrig & 15);
+ memset(p->myximage->data, 0, p->myximage->bytes_per_line * p->image_height);
+ p->ImageData = p->myximage->data;
+#ifdef HAVE_SHM
+}
+#endif
+}
+
+static void freeMyXImage(struct priv *p)
+{
+ struct vo *vo = p->vo;
+#ifdef HAVE_SHM
+ if (p->Shmem_Flag) {
+ XShmDetach(vo->x11->display, &p->Shminfo[0]);
+ XDestroyImage(p->myximage);
+ shmdt(p->Shminfo[0].shmaddr);
+ } else
+#endif
+ {
+ p->myximage->data = p->ImageDataOrig;
+ XDestroyImage(p->myximage);
+ p->ImageDataOrig = NULL;
+ }
+ p->myximage = NULL;
+ p->ImageData = NULL;
+}
+
+#if BYTE_ORDER == BIG_ENDIAN
+#define BO_NATIVE MSBFirst
+#define BO_NONNATIVE LSBFirst
+#else
+#define BO_NATIVE LSBFirst
+#define BO_NONNATIVE MSBFirst
+#endif
+const struct fmt2Xfmtentry_s {
+ uint32_t mpfmt;
+ int byte_order;
+ unsigned red_mask;
+ unsigned green_mask;
+ unsigned blue_mask;
+} fmt2Xfmt[] = {
+ {IMGFMT_RGB8, BO_NATIVE, 0x00000007, 0x00000038, 0x000000C0},
+ {IMGFMT_RGB8, BO_NONNATIVE, 0x00000007, 0x00000038, 0x000000C0},
+ {IMGFMT_BGR8, BO_NATIVE, 0x000000E0, 0x0000001C, 0x00000003},
+ {IMGFMT_BGR8, BO_NONNATIVE, 0x000000E0, 0x0000001C, 0x00000003},
+ {IMGFMT_RGB15, BO_NATIVE, 0x0000001F, 0x000003E0, 0x00007C00},
+ {IMGFMT_BGR15, BO_NATIVE, 0x00007C00, 0x000003E0, 0x0000001F},
+ {IMGFMT_RGB16, BO_NATIVE, 0x0000001F, 0x000007E0, 0x0000F800},
+ {IMGFMT_BGR16, BO_NATIVE, 0x0000F800, 0x000007E0, 0x0000001F},
+ {IMGFMT_RGB24, MSBFirst, 0x00FF0000, 0x0000FF00, 0x000000FF},
+ {IMGFMT_RGB24, LSBFirst, 0x000000FF, 0x0000FF00, 0x00FF0000},
+ {IMGFMT_BGR24, MSBFirst, 0x000000FF, 0x0000FF00, 0x00FF0000},
+ {IMGFMT_BGR24, LSBFirst, 0x00FF0000, 0x0000FF00, 0x000000FF},
+ {IMGFMT_RGB32, BO_NATIVE, 0x000000FF, 0x0000FF00, 0x00FF0000},
+ {IMGFMT_RGB32, BO_NONNATIVE, 0xFF000000, 0x00FF0000, 0x0000FF00},
+ {IMGFMT_BGR32, BO_NATIVE, 0x00FF0000, 0x0000FF00, 0x000000FF},
+ {IMGFMT_BGR32, BO_NONNATIVE, 0x0000FF00, 0x00FF0000, 0xFF000000},
+ {IMGFMT_ARGB, MSBFirst, 0x00FF0000, 0x0000FF00, 0x000000FF},
+ {IMGFMT_ARGB, LSBFirst, 0x0000FF00, 0x00FF0000, 0xFF000000},
+ {IMGFMT_ABGR, MSBFirst, 0x000000FF, 0x0000FF00, 0x00FF0000},
+ {IMGFMT_ABGR, LSBFirst, 0xFF000000, 0x00FF0000, 0x0000FF00},
+ {IMGFMT_RGBA, MSBFirst, 0xFF000000, 0x00FF0000, 0x0000FF00},
+ {IMGFMT_RGBA, LSBFirst, 0x000000FF, 0x0000FF00, 0x00FF0000},
+ {IMGFMT_BGRA, MSBFirst, 0x0000FF00, 0x00FF0000, 0xFF000000},
+ {IMGFMT_BGRA, LSBFirst, 0x00FF0000, 0x0000FF00, 0x000000FF},
+ {0}
+};
+
+static int config(struct vo *vo, uint32_t width, uint32_t height,
+ uint32_t d_width, uint32_t d_height, uint32_t flags,
+ uint32_t format)
+{
+ struct priv *p = vo->priv;
+
+ Colormap theCmap;
+ const struct fmt2Xfmtentry_s *fmte = fmt2Xfmt;
+
+#ifdef CONFIG_XF86VM
+ int vm = flags & VOFLAG_MODESWITCHING;
+#endif
+ p->Flip_Flag = flags & VOFLAG_FLIPPING;
+ p->zoomFlag = flags & VOFLAG_SWSCALE;
+
+ p->old_vo_dwidth = -1;
+ p->old_vo_dheight = -1;
+
+ p->in_format = format;
+ p->srcW = width;
+ p->srcH = height;
+
+ XGetWindowAttributes(vo->x11->display, vo->x11->rootwin, &p->attribs);
+ p->depth = p->attribs.depth;
+
+ if (p->depth != 15 && p->depth != 16 && p->depth != 24 && p->depth != 32) {
+ Visual *visual;
+
+ p->depth = vo_find_depth_from_visuals(vo->x11->display, vo->x11->screen,
+ &visual);
+ }
+ if (!XMatchVisualInfo(vo->x11->display, vo->x11->screen, p->depth,
+ DirectColor, &p->vinfo)
+ || (WinID > 0
+ && p->vinfo.visualid != XVisualIDFromVisual(p->attribs.visual)))
+ {
+ XMatchVisualInfo(vo->x11->display, vo->x11->screen, p->depth, TrueColor,
+ &p->vinfo);
+ }
+
+ /* set image size (which is indeed neither the input nor output size),
+ if zoom is on it will be changed during draw_slice anyway so we don't
+ duplicate the aspect code here
+ */
+ p->image_width = (width + 7) & (~7);
+ p->image_height = height;
+
+ {
+#ifdef CONFIG_XF86VM
+ if (vm)
+ vo_vm_switch(vo);
+
+#endif
+ theCmap = vo_x11_create_colormap(vo, &p->vinfo);
+
+ vo_x11_create_vo_window(vo, &p->vinfo, vo->dx, vo->dy, vo->dwidth,
+ vo->dheight, flags, theCmap, "x11");
+ if (WinID > 0)
+ p->depth = vo_x11_update_geometry(vo, true);
+
+#ifdef CONFIG_XF86VM
+ if (vm) {
+ /* Grab the mouse pointer in our window */
+ if (vo_grabpointer)
+ XGrabPointer(vo->x11->display, vo->x11->window, True, 0,
+ GrabModeAsync, GrabModeAsync,
+ vo->x11->window, None, CurrentTime);
+ XSetInputFocus(vo->x11->display, vo->x11->window, RevertToNone,
+ CurrentTime);
+ }
+#endif
+ }
+
+ if (p->myximage) {
+ freeMyXImage(p);
+ sws_freeContext(p->swsContext);
+ }
+ getMyXImage(p);
+
+ while (fmte->mpfmt) {
+ int depth = IMGFMT_RGB_DEPTH(fmte->mpfmt);
+ /* bits_per_pixel in X seems to be set to 16 for 15 bit formats
+ => force depth to 16 so that only the color masks are used for the format check */
+ if (depth == 15)
+ depth = 16;
+
+ if (depth == p->myximage->bits_per_pixel &&
+ fmte->byte_order == p->myximage->byte_order &&
+ fmte->red_mask == p->myximage->red_mask &&
+ fmte->green_mask == p->myximage->green_mask &&
+ fmte->blue_mask == p->myximage->blue_mask)
+ break;
+ fmte++;
+ }
+ if (!fmte->mpfmt) {
+ mp_msg(
+ MSGT_VO, MSGL_ERR,
+ "X server image format not supported, please contact the developers\n");
+ return -1;
+ }
+ p->out_format = fmte->mpfmt;
+ p->bpp = p->myximage->bits_per_pixel;
+ p->out_offset = 0;
+ // We can easily "emulate" non-native RGB32 and BGR32
+ if (p->out_format == (IMGFMT_BGR32 | 128)
+ || p->out_format == (IMGFMT_RGB32 | 128))
+ {
+ p->out_format &= ~128;
+#if BYTE_ORDER == BIG_ENDIAN
+ p->out_offset = 1;
+#else
+ p->out_offset = -1;
+#endif
+ }
+
+ /* always allocate swsContext as size could change between frames */
+ p->swsContext = sws_getContextFromCmdLine(width, height, p->in_format,
+ width, height, p->out_format);
+ if (!p->swsContext)
+ return -1;
+
+ p->dst_width = width;
+
+ return 0;
+}
+
+static void Display_Image(struct priv *p, XImage *myximage, uint8_t *ImageData)
+{
+ struct vo *vo = p->vo;
+
+ int x = (vo->dwidth - p->dst_width) / 2;
+ int y = (vo->dheight - p->myximage->height) / 2;
+
+ // do not draw if the image needs rescaling
+ if ((p->old_vo_dwidth != vo->dwidth ||
+ p->old_vo_dheight != vo->dheight) && p->zoomFlag)
+ return;
+
+ if (WinID == 0) {
+ x = vo->dx;
+ y = vo->dy;
+ }
+ p->myximage->data += p->out_offset;
+#ifdef HAVE_SHM
+ if (p->Shmem_Flag) {
+ XShmPutImage(vo->x11->display, vo->x11->window, vo->x11->vo_gc,
+ p->myximage, 0, 0, x, y, p->dst_width, p->myximage->height,
+ True);
+ } else
+#endif
+ {
+ XPutImage(vo->x11->display, vo->x11->window, vo->x11->vo_gc,
+ p->myximage, 0, 0, x, y, p->dst_width, p->myximage->height);
+ }
+ p->myximage->data -= p->out_offset;
+}
+
+static struct mp_image get_x_buffer(struct priv *p)
+{
+ struct mp_image img = {0};
+ img.w = img.width = p->image_width;
+ img.h = img.height = p->image_height;
+ mp_image_setfmt(&img, p->out_format);
+
+ img.planes[0] = p->ImageData;
+ img.stride[0] = p->image_width * ((p->bpp + 7) / 8);
+
+ return img;
+}
+
+static void draw_osd(struct vo *vo, struct osd_state *osd)
+{
+ struct priv *p = vo->priv;
+
+ struct mp_image img = get_x_buffer(p);
+
+ struct mp_osd_res res = {
+ .w = img.w,
+ .h = img.h,
+ .display_par = vo->monitor_par,
+ .video_par = vo->aspdat.par,
+ };
+
+ osd_draw_on_image(osd, res, osd->vo_pts, 0, &img);
+}
+
+static void flip_page(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ Display_Image(p, p->myximage, p->ImageData);
+ XSync(vo->x11->display, False);
+}
+
+static int draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h,
+ int x, int y)
+{
+ struct priv *p = vo->priv;
+ uint8_t *dst[MP_MAX_PLANES] = {NULL};
+ int dstStride[MP_MAX_PLANES] = {0};
+
+ if ((p->old_vo_dwidth != vo->dwidth || p->old_vo_dheight != vo->dheight)
+ /*&& y==0 */ && p->zoomFlag)
+ {
+ int newW = vo->dwidth;
+ int newH = vo->dheight;
+ struct SwsContext *oldContext = p->swsContext;
+
+ p->old_vo_dwidth = vo->dwidth;
+ p->old_vo_dheight = vo->dheight;
+
+ if (vo_fs)
+ aspect(vo, &newW, &newH, A_ZOOM);
+ if (sws_flags == 0)
+ newW &= (~31); // not needed but, if the user wants the FAST_BILINEAR SCALER, then its needed
+
+ p->swsContext
+ = sws_getContextFromCmdLine(p->srcW, p->srcH, p->in_format, newW,
+ newH, p->out_format);
+ if (p->swsContext) {
+ p->image_width = (newW + 7) & (~7);
+ p->image_height = newH;
+
+ freeMyXImage(p);
+ getMyXImage(p);
+ sws_freeContext(oldContext);
+ } else
+ p->swsContext = oldContext;
+ p->dst_width = newW;
+ }
+
+ dstStride[0] = p->image_width * ((p->bpp + 7) / 8);
+ dst[0] = p->ImageData;
+ if (p->Flip_Flag) {
+ dst[0] += dstStride[0] * (p->image_height - 1);
+ dstStride[0] = -dstStride[0];
+ }
+ sws_scale(p->swsContext, (const uint8_t **)src, stride, y, h, dst,
+ dstStride);
+ return 0;
+}
+
+static int query_format(struct vo *vo, uint32_t format)
+{
+ mp_msg(MSGT_VO, MSGL_DBG2,
+ "vo_x11: query_format was called: %x (%s)\n", format,
+ vo_format_name(format));
+ if (IMGFMT_IS_BGR(format)) {
+ if (IMGFMT_BGR_DEPTH(format) <= 8)
+ return 0; // TODO 8bpp not yet fully implemented
+ if (IMGFMT_BGR_DEPTH(format) == vo->x11->depthonscreen)
+ return VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW |
+ VFCAP_OSD | VFCAP_SWSCALE | VFCAP_FLIP |
+ VFCAP_ACCEPT_STRIDE;
+ else
+ return VFCAP_CSP_SUPPORTED | VFCAP_OSD | VFCAP_SWSCALE |
+ VFCAP_FLIP |
+ VFCAP_ACCEPT_STRIDE;
+ }
+
+ switch (format) {
+ case IMGFMT_I420:
+ case IMGFMT_IYUV:
+ case IMGFMT_YV12:
+ return VFCAP_CSP_SUPPORTED | VFCAP_OSD | VFCAP_SWSCALE |
+ VFCAP_ACCEPT_STRIDE;
+ }
+ return 0;
+}
+
+
+static void uninit(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ if (p->myximage)
+ freeMyXImage(p);
+
+#ifdef CONFIG_XF86VM
+ vo_vm_close(vo);
+#endif
+
+ p->zoomFlag = 0;
+ vo_x11_uninit(vo);
+
+ sws_freeContext(p->swsContext);
+}
+
+static int preinit(struct vo *vo, const char *arg)
+{
+ struct priv *p = vo->priv;
+ p->vo = vo;
+
+ if (arg) {
+ mp_msg(MSGT_VO, MSGL_ERR, "vo_x11: Unknown subdevice: %s\n", arg);
+ return ENOSYS;
+ }
+
+ if (!vo_init(vo))
+ return -1; // Can't open X11
+ return 0;
+}
+
+static int control(struct vo *vo, uint32_t request, void *data)
+{
+ struct priv *p = vo->priv;
+ switch (request) {
+ case VOCTRL_PAUSE:
+ return p->int_pause = 1;
+ case VOCTRL_RESUME:
+ return p->int_pause = 0;
+ case VOCTRL_QUERY_FORMAT:
+ return query_format(vo, *((uint32_t *) data));
+ case VOCTRL_FULLSCREEN:
+ vo_x11_fullscreen(vo);
+ vo_x11_clearwindow(vo, vo->x11->window);
+ return VO_TRUE;
+ case VOCTRL_SET_EQUALIZER:
+ {
+ struct voctrl_set_equalizer_args *args = data;
+ return vo_x11_set_equalizer(vo, args->name, args->value);
+ }
+ case VOCTRL_GET_EQUALIZER:
+ {
+ struct voctrl_get_equalizer_args *args = data;
+ return vo_x11_get_equalizer(args->name, args->valueptr);
+ }
+ case VOCTRL_ONTOP:
+ vo_x11_ontop(vo);
+ return VO_TRUE;
+ case VOCTRL_UPDATE_SCREENINFO:
+ update_xinerama_info(vo);
+ return VO_TRUE;
+ }
+ return VO_NOTIMPL;
+}
+
+const struct vo_driver video_out_x11 = {
+ .is_new = false,
+ .info = &(const vo_info_t) {
+ "X11 ( XImage/Shm )",
+ "x11",
+ "Aaron Holtzman <aholtzma@ess.engr.uvic.ca>",
+ ""
+ },
+ .priv_size = sizeof(struct priv),
+ .priv_defaults = &(const struct priv) {
+ .srcW = -1,
+ .srcH = -1,
+ .old_vo_dwidth = -1,
+ .old_vo_dheight = -1,
+#ifdef HAVE_SHM
+ .CompletionType = -1,
+#endif
+ },
+ .preinit = preinit,
+ .config = config,
+ .control = control,
+ .draw_slice = draw_slice,
+ .draw_osd = draw_osd,
+ .flip_page = flip_page,
+ .check_events = check_events,
+ .uninit = uninit,
+};
diff --git a/video/out/vo_xv.c b/video/out/vo_xv.c
new file mode 100644
index 0000000000..3673764ed4
--- /dev/null
+++ b/video/out/vo_xv.c
@@ -0,0 +1,716 @@
+/*
+ * X11 Xv interface
+ *
+ * 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 <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include <libavutil/common.h>
+
+#include "config.h"
+
+#ifdef HAVE_SHM
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <X11/extensions/XShm.h>
+#endif
+
+// Note: depends on the inclusion of X11/extensions/XShm.h
+#include <X11/extensions/Xv.h>
+#include <X11/extensions/Xvlib.h>
+
+#include "options.h"
+#include "talloc.h"
+#include "mp_msg.h"
+#include "video_out.h"
+#include "libmpcodecs/vfcap.h"
+#include "libmpcodecs/mp_image.h"
+#include "x11_common.h"
+#include "fastmemcpy.h"
+#include "sub/sub.h"
+#include "aspect.h"
+#include "csputils.h"
+#include "subopt-helper.h"
+
+static const vo_info_t info = {
+ "X11/Xv",
+ "xv",
+ "Gerd Knorr <kraxel@goldbach.in-berlin.de> and others",
+ ""
+};
+
+struct xvctx {
+ XvAdaptorInfo *ai;
+ XvImageFormatValues *fo;
+ unsigned int formats, adaptors, xv_format;
+ int current_buf;
+ int current_ip_buf;
+ int num_buffers;
+ int total_buffers;
+ bool have_image_copy;
+ bool unchanged_image;
+ int visible_buf;
+ XvImage *xvimage[2 + 1];
+ uint32_t image_width;
+ uint32_t image_height;
+ uint32_t image_format;
+ struct mp_csp_details cached_csp;
+ int is_paused;
+ struct mp_rect src_rect;
+ struct mp_rect dst_rect;
+ uint32_t max_width, max_height; // zero means: not set
+ int mode_switched;
+#ifdef HAVE_SHM
+ XShmSegmentInfo Shminfo[2 + 1];
+ int Shmem_Flag;
+#endif
+};
+
+static void allocate_xvimage(struct vo *, int);
+static void deallocate_xvimage(struct vo *vo, int foo);
+
+static void read_xv_csp(struct vo *vo)
+{
+ struct xvctx *ctx = vo->priv;
+ struct vo_x11_state *x11 = vo->x11;
+ struct mp_csp_details *cspc = &ctx->cached_csp;
+ *cspc = (struct mp_csp_details) MP_CSP_DETAILS_DEFAULTS;
+ int bt709_enabled;
+ if (vo_xv_get_eq(vo, x11->xv_port, "bt_709", &bt709_enabled))
+ cspc->format = bt709_enabled == 100 ? MP_CSP_BT_709 : MP_CSP_BT_601;
+}
+
+static void resize(struct vo *vo)
+{
+ struct xvctx *ctx = vo->priv;
+
+ // Can't be used, because the function calculates screen-space coordinates,
+ // while we need video-space.
+ struct mp_osd_res unused;
+
+ vo_get_src_dst_rects(vo, &ctx->src_rect, &ctx->dst_rect, &unused);
+
+ struct mp_rect *dst = &ctx->dst_rect;
+ int dw = dst->x1 - dst->x0, dh = dst->y1 - dst->y0;
+ vo_x11_clearwindow_part(vo, vo->x11->window, dw, dh);
+ vo_xv_draw_colorkey(vo, dst->x0, dst->y0, dw, dh);
+ read_xv_csp(vo);
+}
+
+/*
+ * connect to server, create and map window,
+ * allocate colors and (shared) memory
+ */
+static int config(struct vo *vo, uint32_t width, uint32_t height,
+ uint32_t d_width, uint32_t d_height, uint32_t flags,
+ uint32_t format)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ XVisualInfo vinfo;
+ XSetWindowAttributes xswa;
+ XWindowAttributes attribs;
+ unsigned long xswamask;
+ int depth;
+ struct xvctx *ctx = vo->priv;
+ int i;
+
+ ctx->image_height = height;
+ ctx->image_width = width;
+ ctx->image_format = format;
+
+ if ((ctx->max_width != 0 && ctx->max_height != 0)
+ && (ctx->image_width > ctx->max_width
+ || ctx->image_height > ctx->max_height)) {
+ mp_tmsg(MSGT_VO, MSGL_ERR, "Source image dimensions are too high: %ux%u (maximum is %ux%u)\n",
+ ctx->image_width, ctx->image_height, ctx->max_width,
+ ctx->max_height);
+ return -1;
+ }
+
+ ctx->visible_buf = -1;
+ ctx->have_image_copy = false;
+
+ /* check image formats */
+ ctx->xv_format = 0;
+ for (i = 0; i < ctx->formats; i++) {
+ mp_msg(MSGT_VO, MSGL_V, "Xvideo image format: 0x%x (%4.4s) %s\n",
+ ctx->fo[i].id, (char *) &ctx->fo[i].id,
+ (ctx->fo[i].format == XvPacked) ? "packed" : "planar");
+ if (ctx->fo[i].id == format)
+ ctx->xv_format = ctx->fo[i].id;
+ }
+ if (!ctx->xv_format)
+ return -1;
+
+ {
+#ifdef CONFIG_XF86VM
+ int vm = flags & VOFLAG_MODESWITCHING;
+ if (vm) {
+ vo_vm_switch(vo);
+ ctx->mode_switched = 1;
+ }
+#endif
+ XGetWindowAttributes(x11->display, DefaultRootWindow(x11->display),
+ &attribs);
+ depth = attribs.depth;
+ if (depth != 15 && depth != 16 && depth != 24 && depth != 32)
+ depth = 24;
+ XMatchVisualInfo(x11->display, x11->screen, depth, TrueColor, &vinfo);
+
+ xswa.border_pixel = 0;
+ xswamask = CWBorderPixel;
+ if (x11->xv_ck_info.method == CK_METHOD_BACKGROUND) {
+ xswa.background_pixel = x11->xv_colorkey;
+ xswamask |= CWBackPixel;
+ }
+
+ vo_x11_create_vo_window(vo, &vinfo, vo->dx, vo->dy, vo->dwidth,
+ vo->dheight, flags, CopyFromParent, "xv");
+ XChangeWindowAttributes(x11->display, x11->window, xswamask, &xswa);
+
+#ifdef CONFIG_XF86VM
+ if (vm) {
+ /* Grab the mouse pointer in our window */
+ if (vo_grabpointer)
+ XGrabPointer(x11->display, x11->window, True, 0, GrabModeAsync,
+ GrabModeAsync, x11->window, None, CurrentTime);
+ XSetInputFocus(x11->display, x11->window, RevertToNone,
+ CurrentTime);
+ }
+#endif
+ }
+
+ mp_msg(MSGT_VO, MSGL_V, "using Xvideo port %d for hw scaling\n",
+ x11->xv_port);
+
+ // In case config has been called before
+ for (i = 0; i < ctx->total_buffers; i++)
+ deallocate_xvimage(vo, i);
+
+ ctx->num_buffers = 2;
+ ctx->total_buffers = ctx->num_buffers + 1;
+
+ for (i = 0; i < ctx->total_buffers; i++)
+ allocate_xvimage(vo, i);
+
+ ctx->current_buf = 0;
+ ctx->current_ip_buf = 0;
+
+
+ resize(vo);
+
+ return 0;
+}
+
+static void allocate_xvimage(struct vo *vo, int foo)
+{
+ struct xvctx *ctx = vo->priv;
+ struct vo_x11_state *x11 = vo->x11;
+ /*
+ * allocate XvImages. FIXME: no error checking, without
+ * mit-shm this will bomb... trzing to fix ::atmos
+ */
+#ifdef HAVE_SHM
+ if (x11->display_is_local && XShmQueryExtension(x11->display))
+ ctx->Shmem_Flag = 1;
+ else {
+ ctx->Shmem_Flag = 0;
+ mp_tmsg(MSGT_VO, MSGL_INFO, "[VO_XV] Shared memory not supported\nReverting to normal Xv.\n");
+ }
+ if (ctx->Shmem_Flag) {
+ ctx->xvimage[foo] =
+ (XvImage *) XvShmCreateImage(x11->display, x11->xv_port,
+ ctx->xv_format, NULL,
+ ctx->image_width, ctx->image_height,
+ &ctx->Shminfo[foo]);
+
+ ctx->Shminfo[foo].shmid = shmget(IPC_PRIVATE,
+ ctx->xvimage[foo]->data_size,
+ IPC_CREAT | 0777);
+ ctx->Shminfo[foo].shmaddr = (char *) shmat(ctx->Shminfo[foo].shmid, 0,
+ 0);
+ ctx->Shminfo[foo].readOnly = False;
+
+ ctx->xvimage[foo]->data = ctx->Shminfo[foo].shmaddr;
+ XShmAttach(x11->display, &ctx->Shminfo[foo]);
+ XSync(x11->display, False);
+ shmctl(ctx->Shminfo[foo].shmid, IPC_RMID, 0);
+ } else
+#endif
+ {
+ ctx->xvimage[foo] =
+ (XvImage *) XvCreateImage(x11->display, x11->xv_port,
+ ctx->xv_format, NULL, ctx->image_width,
+ ctx->image_height);
+ ctx->xvimage[foo]->data = malloc(ctx->xvimage[foo]->data_size);
+ XSync(x11->display, False);
+ }
+ memset(ctx->xvimage[foo]->data, 128, ctx->xvimage[foo]->data_size);
+ return;
+}
+
+static void deallocate_xvimage(struct vo *vo, int foo)
+{
+ struct xvctx *ctx = vo->priv;
+#ifdef HAVE_SHM
+ if (ctx->Shmem_Flag) {
+ XShmDetach(vo->x11->display, &ctx->Shminfo[foo]);
+ shmdt(ctx->Shminfo[foo].shmaddr);
+ } else
+#endif
+ {
+ free(ctx->xvimage[foo]->data);
+ }
+ XFree(ctx->xvimage[foo]);
+
+ XSync(vo->x11->display, False);
+ return;
+}
+
+static inline void put_xvimage(struct vo *vo, XvImage *xvi)
+{
+ struct xvctx *ctx = vo->priv;
+ struct vo_x11_state *x11 = vo->x11;
+ struct mp_rect *src = &ctx->src_rect;
+ struct mp_rect *dst = &ctx->dst_rect;
+ int dw = dst->x1 - dst->x0, dh = dst->y1 - dst->y0;
+ int sw = src->x1 - src->x0, sh = src->y1 - src->y0;
+#ifdef HAVE_SHM
+ if (ctx->Shmem_Flag) {
+ XvShmPutImage(x11->display, x11->xv_port, x11->window, x11->vo_gc, xvi,
+ src->x0, src->y0, sw, sh,
+ dst->x0, dst->y0, dw, dh,
+ False);
+ } else
+#endif
+ {
+ XvPutImage(x11->display, x11->xv_port, x11->window, x11->vo_gc, xvi,
+ src->x0, src->y0, sw, sh,
+ dst->x0, dst->y0, dw, dh);
+ }
+}
+
+static struct mp_image get_xv_buffer(struct vo *vo, int buf_index)
+{
+ struct xvctx *ctx = vo->priv;
+ XvImage *xv_image = ctx->xvimage[buf_index];
+
+ struct mp_image img = {0};
+ img.w = img.width = xv_image->width;
+ img.h = img.height = xv_image->height;
+ mp_image_setfmt(&img, ctx->image_format);
+
+ bool swapuv = ctx->image_format == IMGFMT_YV12;
+ for (int n = 0; n < img.num_planes; n++) {
+ int sn = n > 0 && swapuv ? (n == 1 ? 2 : 1) : n;
+ img.planes[n] = xv_image->data + xv_image->offsets[sn];
+ img.stride[n] = xv_image->pitches[sn];
+ }
+
+ mp_image_set_colorspace_details(&img, &ctx->cached_csp);
+
+ return img;
+}
+
+static void copy_backup_image(struct vo *vo, int dest, int src)
+{
+ struct mp_image img_dest = get_xv_buffer(vo, dest);
+ struct mp_image img_src = get_xv_buffer(vo, src);
+
+ copy_mpi(&img_dest, &img_src);
+}
+
+static void check_events(struct vo *vo)
+{
+ int e = vo_x11_check_events(vo);
+
+ if (e & VO_EVENT_EXPOSE || e & VO_EVENT_RESIZE) {
+ resize(vo);
+ vo->want_redraw = true;
+ }
+}
+
+static void draw_osd(struct vo *vo, struct osd_state *osd)
+{
+ struct xvctx *ctx = vo->priv;
+
+ struct mp_image img = get_xv_buffer(vo, ctx->current_buf);
+
+ struct mp_rect *src = &ctx->src_rect;
+ struct mp_rect *dst = &ctx->dst_rect;
+ int dw = dst->x1 - dst->x0, dh = dst->y1 - dst->y0;
+ int sw = src->x1 - src->x0, sh = src->y1 - src->y0;
+ double xvpar = (double)dw / dh * sh / sw;
+
+ struct mp_osd_res res = {
+ .w = ctx->image_width,
+ .h = ctx->image_height,
+ .display_par = vo->monitor_par / xvpar,
+ .video_par = vo->aspdat.par,
+ };
+
+ if (osd_draw_on_image(osd, res, osd->vo_pts, 0, &img))
+ ctx->unchanged_image = false;
+}
+
+static int redraw_frame(struct vo *vo)
+{
+ struct xvctx *ctx = vo->priv;
+
+ if (ctx->have_image_copy)
+ copy_backup_image(vo, ctx->visible_buf, ctx->num_buffers);
+ else if (ctx->unchanged_image) {
+ copy_backup_image(vo, ctx->num_buffers, ctx->visible_buf);
+ ctx->have_image_copy = true;
+ } else
+ return false;
+ ctx->current_buf = ctx->visible_buf;
+ return true;
+}
+
+static void flip_page(struct vo *vo)
+{
+ struct xvctx *ctx = vo->priv;
+ put_xvimage(vo, ctx->xvimage[ctx->current_buf]);
+
+ /* remember the currently visible buffer */
+ ctx->visible_buf = ctx->current_buf;
+
+ ctx->current_buf = (ctx->current_buf + 1) % ctx->num_buffers;
+ XFlush(vo->x11->display);
+ return;
+}
+
+static int draw_slice(struct vo *vo, uint8_t *image[], int stride[], int w,
+ int h, int x, int y)
+{
+ struct xvctx *ctx = vo->priv;
+ uint8_t *dst;
+ XvImage *current_image = ctx->xvimage[ctx->current_buf];
+
+ dst = current_image->data + current_image->offsets[0]
+ + current_image->pitches[0] * y + x;
+ memcpy_pic(dst, image[0], w, h, current_image->pitches[0], stride[0]);
+
+ x /= 2;
+ y /= 2;
+ w /= 2;
+ h /= 2;
+
+ dst = current_image->data + current_image->offsets[1]
+ + current_image->pitches[1] * y + x;
+ if (ctx->image_format != IMGFMT_YV12)
+ memcpy_pic(dst, image[1], w, h, current_image->pitches[1], stride[1]);
+ else
+ memcpy_pic(dst, image[2], w, h, current_image->pitches[1], stride[2]);
+
+ dst = current_image->data + current_image->offsets[2]
+ + current_image->pitches[2] * y + x;
+ if (ctx->image_format == IMGFMT_YV12)
+ memcpy_pic(dst, image[1], w, h, current_image->pitches[1], stride[1]);
+ else
+ memcpy_pic(dst, image[2], w, h, current_image->pitches[1], stride[2]);
+
+ return 0;
+}
+
+static mp_image_t *get_screenshot(struct vo *vo)
+{
+ struct xvctx *ctx = vo->priv;
+
+ // try to get an image without OSD
+ int id = ctx->have_image_copy ? ctx->num_buffers : ctx->visible_buf;
+ struct mp_image img = get_xv_buffer(vo, id);
+ img.display_w = vo->aspdat.prew;
+ img.display_h = vo->aspdat.preh;
+
+ return talloc_memdup(NULL, &img, sizeof(img));
+}
+
+static uint32_t draw_image(struct vo *vo, mp_image_t *mpi)
+{
+ struct xvctx *ctx = vo->priv;
+
+ ctx->have_image_copy = false;
+
+ if (mpi->flags & MP_IMGFLAG_DRAW_CALLBACK)
+ ; // done
+ else if (mpi->flags & MP_IMGFLAG_PLANAR)
+ draw_slice(vo, mpi->planes, mpi->stride, mpi->w, mpi->h, 0, 0);
+ else if (mpi->flags & MP_IMGFLAG_YUV)
+ // packed YUV:
+ memcpy_pic(ctx->xvimage[ctx->current_buf]->data +
+ ctx->xvimage[ctx->current_buf]->offsets[0], mpi->planes[0],
+ mpi->w * (mpi->bpp / 8), mpi->h,
+ ctx->xvimage[ctx->current_buf]->pitches[0], mpi->stride[0]);
+ else
+ return false;
+
+ if (ctx->is_paused) {
+ copy_backup_image(vo, ctx->num_buffers, ctx->current_buf);
+ ctx->have_image_copy = true;
+ }
+ ctx->unchanged_image = true;
+ return true;
+}
+
+static int query_format(struct xvctx *ctx, uint32_t format)
+{
+ uint32_t i;
+ int flag = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | VFCAP_OSD | VFCAP_ACCEPT_STRIDE; // FIXME! check for DOWN
+
+ /* check image formats */
+ for (i = 0; i < ctx->formats; i++) {
+ if (ctx->fo[i].id == format)
+ return flag; //xv_format = fo[i].id;
+ }
+ return 0;
+}
+
+static void uninit(struct vo *vo)
+{
+ struct xvctx *ctx = vo->priv;
+ int i;
+
+ ctx->visible_buf = -1;
+ if (ctx->ai)
+ XvFreeAdaptorInfo(ctx->ai);
+ ctx->ai = NULL;
+ if (ctx->fo) {
+ XFree(ctx->fo);
+ ctx->fo = NULL;
+ }
+ for (i = 0; i < ctx->total_buffers; i++)
+ deallocate_xvimage(vo, i);
+#ifdef CONFIG_XF86VM
+ if (ctx->mode_switched)
+ vo_vm_close(vo);
+#endif
+ // uninit() shouldn't get called unless initialization went past vo_init()
+ vo_x11_uninit(vo);
+}
+
+static int preinit(struct vo *vo, const char *arg)
+{
+ XvPortID xv_p;
+ int busy_ports = 0;
+ unsigned int i;
+ strarg_t ck_src_arg = { 0, NULL };
+ strarg_t ck_method_arg = { 0, NULL };
+ struct xvctx *ctx = talloc_zero(vo, struct xvctx);
+ vo->priv = ctx;
+ int xv_adaptor = -1;
+
+ if (!vo_init(vo))
+ return -1;
+
+ struct vo_x11_state *x11 = vo->x11;
+
+ const opt_t subopts[] =
+ {
+ /* name arg type arg var test */
+ { "port", OPT_ARG_INT, &x11->xv_port, int_pos },
+ { "adaptor", OPT_ARG_INT, &xv_adaptor, int_non_neg },
+ { "ck", OPT_ARG_STR, &ck_src_arg, xv_test_ck },
+ { "ck-method", OPT_ARG_STR, &ck_method_arg, xv_test_ckm },
+ { NULL }
+ };
+
+ x11->xv_port = 0;
+
+ /* parse suboptions */
+ if (subopt_parse(arg, subopts) != 0) {
+ return -1;
+ }
+
+ /* modify colorkey settings according to the given options */
+ xv_setup_colorkeyhandling(vo, ck_method_arg.str, ck_src_arg.str);
+
+ /* check for Xvideo extension */
+ unsigned int ver, rel, req, ev, err;
+ if (Success != XvQueryExtension(x11->display, &ver, &rel, &req, &ev, &err)) {
+ mp_tmsg(MSGT_VO, MSGL_ERR, "[VO_XV] Sorry, Xv not supported by this X11 version/driver\n[VO_XV] ******** Try with -vo x11 *********\n");
+ goto error;
+ }
+
+ /* check for Xvideo support */
+ if (Success !=
+ XvQueryAdaptors(x11->display, DefaultRootWindow(x11->display),
+ &ctx->adaptors, &ctx->ai)) {
+ mp_tmsg(MSGT_VO, MSGL_ERR, "[VO_XV] XvQueryAdaptors failed.\n");
+ goto error;
+ }
+
+ /* check adaptors */
+ if (x11->xv_port) {
+ int port_found;
+
+ for (port_found = 0, i = 0; !port_found && i < ctx->adaptors; i++) {
+ if ((ctx->ai[i].type & XvInputMask)
+ && (ctx->ai[i].type & XvImageMask)) {
+ for (xv_p = ctx->ai[i].base_id;
+ xv_p < ctx->ai[i].base_id + ctx->ai[i].num_ports;
+ ++xv_p) {
+ if (xv_p == x11->xv_port) {
+ port_found = 1;
+ break;
+ }
+ }
+ }
+ }
+ if (port_found) {
+ if (XvGrabPort(x11->display, x11->xv_port, CurrentTime))
+ x11->xv_port = 0;
+ } else {
+ mp_tmsg(MSGT_VO, MSGL_WARN, "[VO_XV] Invalid port parameter, overriding with port 0.\n");
+ x11->xv_port = 0;
+ }
+ }
+
+ for (i = 0; i < ctx->adaptors && x11->xv_port == 0; i++) {
+ /* check if adaptor number has been specified */
+ if (xv_adaptor != -1 && xv_adaptor != i)
+ continue;
+
+ if ((ctx->ai[i].type & XvInputMask) && (ctx->ai[i].type & XvImageMask)) {
+ for (xv_p = ctx->ai[i].base_id;
+ xv_p < ctx->ai[i].base_id + ctx->ai[i].num_ports; ++xv_p)
+ if (!XvGrabPort(x11->display, xv_p, CurrentTime)) {
+ x11->xv_port = xv_p;
+ mp_msg(MSGT_VO, MSGL_V,
+ "[VO_XV] Using Xv Adapter #%d (%s)\n",
+ i, ctx->ai[i].name);
+ break;
+ } else {
+ mp_tmsg(MSGT_VO, MSGL_WARN, "[VO_XV] Could not grab port %i.\n",
+ (int) xv_p);
+ ++busy_ports;
+ }
+ }
+ }
+ if (!x11->xv_port) {
+ if (busy_ports)
+ mp_tmsg(MSGT_VO, MSGL_ERR,
+ "[VO_XV] Could not find free Xvideo port - maybe another process is already\n"\
+ "[VO_XV] using it. Close all video applications, and try again. If that does\n"\
+ "[VO_XV] not help, see 'mpv -vo help' for other (non-xv) video out drivers.\n");
+ else
+ mp_tmsg(MSGT_VO, MSGL_ERR,
+ "[VO_XV] It seems there is no Xvideo support for your video card available.\n"\
+ "[VO_XV] Run 'xvinfo' to verify its Xv support and read\n"\
+ "[VO_XV] DOCS/HTML/en/video.html#xv!\n"\
+ "[VO_XV] See 'mpv -vo help' for other (non-xv) video out drivers.\n"\
+ "[VO_XV] Try -vo x11.\n");
+ goto error;
+ }
+
+ if (!vo_xv_init_colorkey(vo)) {
+ goto error; // bail out, colorkey setup failed
+ }
+ vo_xv_enable_vsync(vo);
+ vo_xv_get_max_img_dim(vo, &ctx->max_width, &ctx->max_height);
+
+ ctx->fo = XvListImageFormats(x11->display, x11->xv_port,
+ (int *) &ctx->formats);
+
+ return 0;
+
+ error:
+ uninit(vo); // free resources
+ return -1;
+}
+
+static int control(struct vo *vo, uint32_t request, void *data)
+{
+ struct xvctx *ctx = vo->priv;
+ struct vo_x11_state *x11 = vo->x11;
+ switch (request) {
+ case VOCTRL_PAUSE:
+ return (ctx->is_paused = 1);
+ case VOCTRL_RESUME:
+ return (ctx->is_paused = 0);
+ case VOCTRL_QUERY_FORMAT:
+ return query_format(ctx, *((uint32_t *) data));
+ case VOCTRL_DRAW_IMAGE:
+ return draw_image(vo, data);
+ case VOCTRL_GET_PANSCAN:
+ return VO_TRUE;
+ case VOCTRL_FULLSCREEN:
+ vo_x11_fullscreen(vo);
+ /* indended, fallthrough to update panscan on fullscreen/windowed switch */
+ case VOCTRL_SET_PANSCAN:
+ resize(vo);
+ return VO_TRUE;
+ case VOCTRL_SET_EQUALIZER: {
+ vo->want_redraw = true;
+ struct voctrl_set_equalizer_args *args = data;
+ return vo_xv_set_eq(vo, x11->xv_port, args->name, args->value);
+ }
+ case VOCTRL_GET_EQUALIZER: {
+ struct voctrl_get_equalizer_args *args = data;
+ return vo_xv_get_eq(vo, x11->xv_port, args->name, args->valueptr);
+ }
+ case VOCTRL_SET_YUV_COLORSPACE:;
+ struct mp_csp_details* given_cspc = data;
+ int is_709 = given_cspc->format == MP_CSP_BT_709;
+ vo_xv_set_eq(vo, x11->xv_port, "bt_709", is_709 * 200 - 100);
+ read_xv_csp(vo);
+ vo->want_redraw = true;
+ return true;
+ case VOCTRL_GET_YUV_COLORSPACE:;
+ struct mp_csp_details* cspc = data;
+ read_xv_csp(vo);
+ *cspc = ctx->cached_csp;
+ return true;
+ case VOCTRL_ONTOP:
+ vo_x11_ontop(vo);
+ return VO_TRUE;
+ case VOCTRL_UPDATE_SCREENINFO:
+ update_xinerama_info(vo);
+ return VO_TRUE;
+ case VOCTRL_REDRAW_FRAME:
+ return redraw_frame(vo);
+ case VOCTRL_SCREENSHOT: {
+ struct voctrl_screenshot_args *args = data;
+ args->out_image = get_screenshot(vo);
+ args->has_osd = !ctx->have_image_copy;
+ return true;
+ }
+ }
+ return VO_NOTIMPL;
+}
+
+const struct vo_driver video_out_xv = {
+ .is_new = 1,
+ .info = &info,
+ .preinit = preinit,
+ .config = config,
+ .control = control,
+ .draw_slice = draw_slice,
+ .draw_osd = draw_osd,
+ .flip_page = flip_page,
+ .check_events = check_events,
+ .uninit = uninit
+};
diff --git a/video/out/w32_common.c b/video/out/w32_common.c
new file mode 100644
index 0000000000..f60f5328de
--- /dev/null
+++ b/video/out/w32_common.c
@@ -0,0 +1,757 @@
+/*
+ * 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 <limits.h>
+#include <assert.h>
+#include <windows.h>
+#include <windowsx.h>
+
+#include "options.h"
+#include "input/keycodes.h"
+#include "input/input.h"
+#include "mp_msg.h"
+#include "video_out.h"
+#include "aspect.h"
+#include "w32_common.h"
+#include "mp_fifo.h"
+#include "osdep/io.h"
+#include "talloc.h"
+
+#define WIN_ID_TO_HWND(x) ((HWND)(uint32_t)(x))
+
+static const wchar_t classname[] = L"mpv";
+
+static const struct mp_keymap vk_map[] = {
+ // special keys
+ {VK_ESCAPE, KEY_ESC}, {VK_BACK, KEY_BS}, {VK_TAB, KEY_TAB},
+ {VK_RETURN, KEY_ENTER}, {VK_PAUSE, KEY_PAUSE}, {VK_SNAPSHOT, KEY_PRINT},
+
+ // cursor keys
+ {VK_LEFT, KEY_LEFT}, {VK_UP, KEY_UP}, {VK_RIGHT, KEY_RIGHT}, {VK_DOWN, KEY_DOWN},
+
+ // navigation block
+ {VK_INSERT, KEY_INSERT}, {VK_DELETE, KEY_DELETE}, {VK_HOME, KEY_HOME}, {VK_END, KEY_END},
+ {VK_PRIOR, KEY_PAGE_UP}, {VK_NEXT, KEY_PAGE_DOWN},
+
+ // F-keys
+ {VK_F1, KEY_F+1}, {VK_F2, KEY_F+2}, {VK_F3, KEY_F+3}, {VK_F4, KEY_F+4},
+ {VK_F5, KEY_F+5}, {VK_F6, KEY_F+6}, {VK_F7, KEY_F+7}, {VK_F8, KEY_F+8},
+ {VK_F9, KEY_F+9}, {VK_F10, KEY_F+10}, {VK_F11, KEY_F+11}, {VK_F12, KEY_F+12},
+ // numpad
+ {VK_NUMPAD0, KEY_KP0}, {VK_NUMPAD1, KEY_KP1}, {VK_NUMPAD2, KEY_KP2},
+ {VK_NUMPAD3, KEY_KP3}, {VK_NUMPAD4, KEY_KP4}, {VK_NUMPAD5, KEY_KP5},
+ {VK_NUMPAD6, KEY_KP6}, {VK_NUMPAD7, KEY_KP7}, {VK_NUMPAD8, KEY_KP8},
+ {VK_NUMPAD9, KEY_KP9}, {VK_DECIMAL, KEY_KPDEC},
+
+ {0, 0}
+};
+
+static void add_window_borders(HWND hwnd, RECT *rc)
+{
+ AdjustWindowRect(rc, GetWindowLong(hwnd, GWL_STYLE), 0);
+}
+
+// basically a reverse AdjustWindowRect (win32 doesn't appear to have this)
+static void subtract_window_borders(HWND hwnd, RECT *rc)
+{
+ RECT b = { 0, 0, 0, 0 };
+ add_window_borders(hwnd, &b);
+ rc->left -= b.left;
+ rc->top -= b.top;
+ rc->right -= b.right;
+ rc->bottom -= b.bottom;
+}
+
+// turn a WMSZ_* input value in v into the border that should be resized
+// returns: 0=left, 1=top, 2=right, 3=bottom, -1=undefined
+static int get_resize_border(int v) {
+ switch (v) {
+ case WMSZ_LEFT: return 3;
+ case WMSZ_TOP: return 2;
+ case WMSZ_RIGHT: return 3;
+ case WMSZ_BOTTOM: return 2;
+ case WMSZ_TOPLEFT: return 1;
+ case WMSZ_TOPRIGHT: return 1;
+ case WMSZ_BOTTOMLEFT: return 3;
+ case WMSZ_BOTTOMRIGHT: return 3;
+ default: return -1;
+ }
+}
+
+static bool key_state(struct vo *vo, int vk)
+{
+ return GetKeyState(vk) & 0x8000;
+}
+
+static int mod_state(struct vo *vo)
+{
+ int res = 0;
+ if (key_state(vo, VK_CONTROL))
+ res |= KEY_MODIFIER_CTRL;
+ if (key_state(vo, VK_SHIFT))
+ res |= KEY_MODIFIER_SHIFT;
+ if (key_state(vo, VK_MENU))
+ res |= KEY_MODIFIER_ALT;
+ return res;
+}
+
+static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
+ LPARAM lParam)
+{
+ if (message == WM_NCCREATE) {
+ CREATESTRUCT *cs = (void*)lParam;
+ SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LONG_PTR)cs->lpCreateParams);
+ }
+ struct vo *vo = (void*)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
+ // message before WM_NCCREATE, pray to Raymond Chen that it's not important
+ if (!vo)
+ return DefWindowProcW(hWnd, message, wParam, lParam);
+ struct vo_w32_state *w32 = vo->w32;
+
+ switch (message) {
+ case WM_ERASEBKGND: // no need to erase background seperately
+ return 1;
+ case WM_PAINT:
+ w32->event_flags |= VO_EVENT_EXPOSE;
+ break;
+ case WM_MOVE: {
+ w32->event_flags |= VO_EVENT_MOVE;
+ POINT p = {0};
+ ClientToScreen(w32->window, &p);
+ w32->window_x = p.x;
+ w32->window_y = p.y;
+ mp_msg(MSGT_VO, MSGL_V, "[vo] move window: %d:%d\n",
+ w32->window_x, w32->window_y);
+ break;
+ }
+ case WM_SIZE: {
+ w32->event_flags |= VO_EVENT_RESIZE;
+ RECT r;
+ GetClientRect(w32->window, &r);
+ vo->dwidth = r.right;
+ vo->dheight = r.bottom;
+ mp_msg(MSGT_VO, MSGL_V, "[vo] resize window: %d:%d\n",
+ vo->dwidth, vo->dheight);
+ break;
+ }
+ case WM_SIZING:
+ if (vo_keepaspect && !vo_fs && WinID < 0) {
+ RECT *rc = (RECT*)lParam;
+ // get client area of the windows if it had the rect rc
+ // (subtracting the window borders)
+ RECT r = *rc;
+ subtract_window_borders(w32->window, &r);
+ int c_w = r.right - r.left, c_h = r.bottom - r.top;
+ float aspect = vo->aspdat.asp;
+ int d_w = c_h * aspect - c_w;
+ int d_h = c_w / aspect - c_h;
+ int d_corners[4] = { d_w, d_h, -d_w, -d_h };
+ int corners[4] = { rc->left, rc->top, rc->right, rc->bottom };
+ int corner = get_resize_border(wParam);
+ if (corner >= 0)
+ corners[corner] -= d_corners[corner];
+ *rc = (RECT) { corners[0], corners[1], corners[2], corners[3] };
+ return TRUE;
+ }
+ break;
+ case WM_CLOSE:
+ mplayer_put_key(vo->key_fifo, KEY_CLOSE_WIN);
+ break;
+ case WM_SYSCOMMAND:
+ switch (wParam) {
+ case SC_SCREENSAVE:
+ case SC_MONITORPOWER:
+ mp_msg(MSGT_VO, MSGL_V, "vo: win32: killing screensaver\n");
+ return 0;
+ }
+ break;
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN: {
+ int mpkey = lookup_keymap_table(vk_map, wParam);
+ if (mpkey)
+ mplayer_put_key(vo->key_fifo, mpkey | mod_state(vo));
+ if (wParam == VK_F10)
+ return 0;
+ break;
+ }
+ case WM_CHAR:
+ case WM_SYSCHAR: {
+ int mods = mod_state(vo);
+ int code = wParam;
+ // Windows enables Ctrl+Alt when AltGr (VK_RMENU) is pressed.
+ // E.g. AltGr+9 on a German keyboard would yield Ctrl+Alt+[
+ // Warning: wine handles this differently. Don't test this on wine!
+ if (key_state(vo, VK_RMENU))
+ mods &= ~(KEY_MODIFIER_CTRL | KEY_MODIFIER_ALT);
+ // Apparently Ctrl+A to Ctrl+Z is special cased, and produces
+ // character codes from 1-26. Work it around.
+ // Also, enter/return (including the keypad variant) and CTRL+J both
+ // map to wParam==10. As a workaround, check VK_RETURN to
+ // distinguish these two key combinations.
+ if ((mods & KEY_MODIFIER_CTRL) && code >= 1 && code <= 26
+ && !key_state(vo, VK_RETURN))
+ code = code - 1 + (mods & KEY_MODIFIER_SHIFT ? 'A' : 'a');
+ if (code >= 32 && code < (1<<21)) {
+ mplayer_put_key(vo->key_fifo, code | mods);
+ // At least with Alt+char, not calling DefWindowProcW stops
+ // Windows from emitting a beep.
+ return 0;
+ }
+ break;
+ }
+ case WM_LBUTTONDOWN:
+ if (!vo_nomouse_input && (vo_fs || (wParam & MK_CONTROL))) {
+ mplayer_put_key(vo->key_fifo, MOUSE_BTN0 | mod_state(vo));
+ break;
+ }
+ if (!vo_fs) {
+ ReleaseCapture();
+ SendMessage(hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
+ return 0;
+ }
+ break;
+ case WM_MBUTTONDOWN:
+ if (!vo_nomouse_input)
+ mplayer_put_key(vo->key_fifo, MOUSE_BTN1 | mod_state(vo));
+ break;
+ case WM_RBUTTONDOWN:
+ if (!vo_nomouse_input)
+ mplayer_put_key(vo->key_fifo, MOUSE_BTN2 | mod_state(vo));
+ break;
+ case WM_MOUSEMOVE:
+ vo_mouse_movement(vo, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+ break;
+ case WM_MOUSEWHEEL:
+ if (!vo_nomouse_input) {
+ int x = GET_WHEEL_DELTA_WPARAM(wParam);
+ if (x > 0)
+ mplayer_put_key(vo->key_fifo, MOUSE_BTN3 | mod_state(vo));
+ else
+ mplayer_put_key(vo->key_fifo, MOUSE_BTN4 | mod_state(vo));
+ }
+ break;
+ case WM_XBUTTONDOWN:
+ if (!vo_nomouse_input) {
+ int x = HIWORD(wParam);
+ if (x == 1)
+ mplayer_put_key(vo->key_fifo, MOUSE_BTN5 | mod_state(vo));
+ else // if (x == 2)
+ mplayer_put_key(vo->key_fifo, MOUSE_BTN6 | mod_state(vo));
+ }
+ break;
+ }
+
+ return DefWindowProcW(hWnd, message, wParam, lParam);
+}
+
+/**
+ * \brief Dispatch incoming window events and handle them.
+ *
+ * This function should be placed inside libvo's function "check_events".
+ *
+ * \return int with these flags possibly set, take care to handle in the right order
+ * if it matters in your driver:
+ *
+ * VO_EVENT_RESIZE = The window was resized. If necessary reinit your
+ * driver render context accordingly.
+ * VO_EVENT_EXPOSE = The window was exposed. Call e.g. flip_frame() to redraw
+ * the window if the movie is paused.
+ */
+int vo_w32_check_events(struct vo *vo)
+{
+ struct vo_w32_state *w32 = vo->w32;
+ MSG msg;
+ w32->event_flags = 0;
+ while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+ if (WinID >= 0) {
+ BOOL res;
+ RECT r;
+ POINT p;
+ res = GetClientRect(w32->window, &r);
+ if (res && (r.right != vo->dwidth || r.bottom != vo->dheight)) {
+ vo->dwidth = r.right; vo->dheight = r.bottom;
+ w32->event_flags |= VO_EVENT_RESIZE;
+ }
+ p.x = 0; p.y = 0;
+ ClientToScreen(w32->window, &p);
+ if (p.x != w32->window_x || p.y != w32->window_y) {
+ w32->window_x = p.x; w32->window_y = p.y;
+ w32->event_flags |= VO_EVENT_MOVE;
+ }
+ res = GetClientRect(WIN_ID_TO_HWND(WinID), &r);
+ if (res && (r.right != vo->dwidth || r.bottom != vo->dheight))
+ MoveWindow(w32->window, 0, 0, r.right, r.bottom, FALSE);
+ if (!IsWindow(WIN_ID_TO_HWND(WinID)))
+ // Window has probably been closed, e.g. due to program crash
+ mplayer_put_key(vo->key_fifo, KEY_CLOSE_WIN);
+ }
+
+ return w32->event_flags;
+}
+
+static BOOL CALLBACK mon_enum(HMONITOR hmon, HDC hdc, LPRECT r, LPARAM p)
+{
+ struct vo *vo = (void*)p;
+ struct vo_w32_state *w32 = vo->w32;
+ // this defaults to the last screen if specified number does not exist
+ xinerama_x = r->left;
+ xinerama_y = r->top;
+ vo->opts->vo_screenwidth = r->right - r->left;
+ vo->opts->vo_screenheight = r->bottom - r->top;
+ if (w32->mon_cnt == xinerama_screen)
+ return FALSE;
+ w32->mon_cnt++;
+ return TRUE;
+}
+
+/**
+ * \brief Update screen information.
+ *
+ * This function should be called in libvo's "control" callback
+ * with parameter VOCTRL_UPDATE_SCREENINFO.
+ * Note that this also enables the new API where geometry and aspect
+ * calculations are done in video_out.c:config_video_out
+ *
+ * Global libvo variables changed:
+ * xinerama_x
+ * xinerama_y
+ * vo_screenwidth
+ * vo_screenheight
+ */
+void w32_update_xinerama_info(struct vo *vo)
+{
+ struct vo_w32_state *w32 = vo->w32;
+ xinerama_x = xinerama_y = 0;
+ if (xinerama_screen < -1) {
+ int tmp;
+ xinerama_x = GetSystemMetrics(SM_XVIRTUALSCREEN);
+ xinerama_y = GetSystemMetrics(SM_YVIRTUALSCREEN);
+ tmp = GetSystemMetrics(SM_CXVIRTUALSCREEN);
+ if (tmp) vo->opts->vo_screenwidth = tmp;
+ tmp = GetSystemMetrics(SM_CYVIRTUALSCREEN);
+ if (tmp) vo->opts->vo_screenheight = tmp;
+ } else if (xinerama_screen == -1) {
+ MONITORINFO mi;
+ HMONITOR m = MonitorFromWindow(w32->window, MONITOR_DEFAULTTOPRIMARY);
+ mi.cbSize = sizeof(mi);
+ GetMonitorInfoW(m, &mi);
+ xinerama_x = mi.rcMonitor.left;
+ xinerama_y = mi.rcMonitor.top;
+ vo->opts->vo_screenwidth = mi.rcMonitor.right - mi.rcMonitor.left;
+ vo->opts->vo_screenheight = mi.rcMonitor.bottom - mi.rcMonitor.top;
+ } else if (xinerama_screen > 0) {
+ w32->mon_cnt = 0;
+ EnumDisplayMonitors(NULL, NULL, mon_enum, (LONG_PTR)vo);
+ }
+ aspect_save_screenres(vo, vo->opts->vo_screenwidth,
+ vo->opts->vo_screenheight);
+}
+
+static void updateScreenProperties(struct vo *vo)
+{
+ struct vo_w32_state *w32 = vo->w32;
+ DEVMODE dm;
+ dm.dmSize = sizeof dm;
+ dm.dmDriverExtra = 0;
+ dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
+ if (!EnumDisplaySettings(0, ENUM_CURRENT_SETTINGS, &dm)) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "vo: win32: unable to enumerate display settings!\n");
+ return;
+ }
+
+ vo->opts->vo_screenwidth = dm.dmPelsWidth;
+ vo->opts->vo_screenheight = dm.dmPelsHeight;
+ w32->depthonscreen = dm.dmBitsPerPel;
+ w32_update_xinerama_info(vo);
+}
+
+static void changeMode(struct vo *vo)
+{
+ struct vo_w32_state *w32 = vo->w32;
+ DEVMODE dm;
+ dm.dmSize = sizeof dm;
+ dm.dmDriverExtra = 0;
+
+ dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
+ dm.dmBitsPerPel = w32->depthonscreen;
+ dm.dmPelsWidth = vo->opts->vo_screenwidth;
+ dm.dmPelsHeight = vo->opts->vo_screenheight;
+
+ if (w32->vm) {
+ int bestMode = -1;
+ int bestScore = INT_MAX;
+ int i;
+ for (i = 0; EnumDisplaySettings(0, i, &dm); ++i) {
+ int score = (dm.dmPelsWidth - w32->o_dwidth)
+ * (dm.dmPelsHeight - w32->o_dheight);
+ if (dm.dmBitsPerPel != w32->depthonscreen
+ || dm.dmPelsWidth < w32->o_dwidth
+ || dm.dmPelsHeight < w32->o_dheight)
+ continue;
+
+ if (score < bestScore) {
+ bestScore = score;
+ bestMode = i;
+ }
+ }
+
+ if (bestMode != -1)
+ EnumDisplaySettings(0, bestMode, &dm);
+
+ ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
+ }
+}
+
+static void resetMode(struct vo *vo)
+{
+ struct vo_w32_state *w32 = vo->w32;
+ if (w32->vm)
+ ChangeDisplaySettings(0, 0);
+}
+
+static DWORD update_style(struct vo *vo, DWORD style)
+{
+ const DWORD NO_FRAME = WS_POPUP;
+ const DWORD FRAME = WS_OVERLAPPEDWINDOW | WS_SIZEBOX;
+ style &= ~(NO_FRAME | FRAME);
+ style |= (vo_border && !vo_fs) ? FRAME : NO_FRAME;
+ return style;
+}
+
+// Update the window title, position, size, and border style from vo_* values.
+static int reinit_window_state(struct vo *vo)
+{
+ struct vo_w32_state *w32 = vo->w32;
+ HWND layer = HWND_NOTOPMOST;
+ RECT r;
+
+ if (WinID >= 0)
+ return 1;
+
+ wchar_t *title = mp_from_utf8(NULL, vo_get_window_title(vo));
+ SetWindowTextW(w32->window, title);
+ talloc_free(title);
+
+ bool toggle_fs = w32->current_fs != vo_fs;
+ w32->current_fs = vo_fs;
+
+ DWORD style = update_style(vo, GetWindowLong(w32->window, GWL_STYLE));
+
+ if (vo_fs || vo->opts->vo_ontop)
+ layer = HWND_TOPMOST;
+
+ // xxx not sure if this can trigger any unwanted messages (WM_MOVE/WM_SIZE)
+ if (vo_fs) {
+ changeMode(vo);
+ while (ShowCursor(0) >= 0) /**/ ;
+ } else {
+ resetMode(vo);
+ while (ShowCursor(1) < 0) /**/ ;
+ }
+ updateScreenProperties(vo);
+
+ if (vo_fs) {
+ // Save window position and size when switching to fullscreen.
+ if (toggle_fs) {
+ w32->prev_width = vo->dwidth;
+ w32->prev_height = vo->dheight;
+ w32->prev_x = w32->window_x;
+ w32->prev_y = w32->window_y;
+ mp_msg(MSGT_VO, MSGL_V, "[vo] save window bounds: %d:%d:%d:%d\n",
+ w32->prev_x, w32->prev_y, w32->prev_width, w32->prev_height);
+ }
+ vo->dwidth = vo->opts->vo_screenwidth;
+ vo->dheight = vo->opts->vo_screenheight;
+ w32->window_x = xinerama_x;
+ w32->window_y = xinerama_y;
+ } else {
+ if (toggle_fs) {
+ // Restore window position and size when switching from fullscreen.
+ mp_msg(MSGT_VO, MSGL_V, "[vo] restore window bounds: %d:%d:%d:%d\n",
+ w32->prev_x, w32->prev_y, w32->prev_width, w32->prev_height);
+ vo->dwidth = w32->prev_width;
+ vo->dheight = w32->prev_height;
+ w32->window_x = w32->prev_x;
+ w32->window_y = w32->prev_y;
+ }
+ }
+
+ r.left = w32->window_x;
+ r.right = r.left + vo->dwidth;
+ r.top = w32->window_y;
+ r.bottom = r.top + vo->dheight;
+
+ SetWindowLong(w32->window, GWL_STYLE, style);
+ add_window_borders(w32->window, &r);
+
+ mp_msg(MSGT_VO, MSGL_V, "[vo] reset window bounds: %ld:%ld:%ld:%ld\n",
+ r.left, r.top, r.right - r.left, r.bottom - r.top);
+
+ SetWindowPos(w32->window, layer, r.left, r.top, r.right - r.left,
+ r.bottom - r.top, SWP_FRAMECHANGED);
+ // For some reason, moving SWP_SHOWWINDOW to a second call works better
+ // with wine: returning from fullscreen doesn't cause a bogus resize to
+ // screen size.
+ // It's not needed on Windows XP or wine with a virtual desktop.
+ // It doesn't seem to have any negative effects.
+ SetWindowPos(w32->window, NULL, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
+
+ return 1;
+}
+
+/**
+ * \brief Configure and show window on the screen.
+ *
+ * This function should be called in libvo's "config" callback.
+ * It configures a window and shows it on the screen.
+ *
+ * \return 1 - Success, 0 - Failure
+ */
+int vo_w32_config(struct vo *vo, uint32_t width, uint32_t height,
+ uint32_t flags)
+{
+ struct vo_w32_state *w32 = vo->w32;
+ PIXELFORMATDESCRIPTOR pfd;
+ int pf;
+ HDC vo_hdc = vo_w32_get_dc(vo, w32->window);
+
+ memset(&pfd, 0, sizeof pfd);
+ pfd.nSize = sizeof pfd;
+ pfd.nVersion = 1;
+ pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
+ if (flags & VOFLAG_STEREO)
+ pfd.dwFlags |= PFD_STEREO;
+ pfd.iPixelType = PFD_TYPE_RGBA;
+ pfd.cColorBits = 24;
+ pfd.iLayerType = PFD_MAIN_PLANE;
+ pf = ChoosePixelFormat(vo_hdc, &pfd);
+ if (!pf) {
+ mp_msg(MSGT_VO, MSGL_ERR, "vo: win32: unable to select a valid pixel format!\n");
+ vo_w32_release_dc(vo, w32->window, vo_hdc);
+ return 0;
+ }
+
+ SetPixelFormat(vo_hdc, pf, &pfd);
+ vo_w32_release_dc(vo, w32->window, vo_hdc);
+
+ // we already have a fully initialized window, so nothing needs to be done
+ if (flags & VOFLAG_HIDDEN)
+ return 1;
+
+ bool reset_size = !(w32->o_dwidth == width && w32->o_dheight == height);
+
+ w32->o_dwidth = width;
+ w32->o_dheight = height;
+
+ // the desired size is ignored in wid mode, it always matches the window size.
+ if (WinID < 0) {
+ if (w32->window_bounds_initialized) {
+ // restore vo_dwidth/vo_dheight, which are reset against our will
+ // in vo_config()
+ RECT r;
+ GetClientRect(w32->window, &r);
+ vo->dwidth = r.right;
+ vo->dheight = r.bottom;
+ } else {
+ // first vo_config call; vo_config() will always set vo_dx/dy so
+ // that the window is centered on the screen, and this is the only
+ // time we actually want to use vo_dy/dy (this is not sane, and
+ // video_out.h should provide a function to query the initial
+ // window position instead)
+ w32->window_bounds_initialized = true;
+ reset_size = true;
+ w32->window_x = w32->prev_x = vo->dx;
+ w32->window_y = w32->prev_y = vo->dy;
+ }
+ if (reset_size) {
+ w32->prev_width = vo->dwidth = width;
+ w32->prev_height = vo->dheight = height;
+ }
+ }
+
+ vo_fs = flags & VOFLAG_FULLSCREEN;
+ w32->vm = flags & VOFLAG_MODESWITCHING;
+ return reinit_window_state(vo);
+}
+
+/**
+ * \brief Initialize w32_common framework.
+ *
+ * The first function that should be called from the w32_common framework.
+ * It handles window creation on the screen with proper title and attributes.
+ * It also initializes the framework's internal variables. The function should
+ * be called after your own preinit initialization and you shouldn't do any
+ * window management on your own.
+ *
+ * Global libvo variables changed:
+ * vo_w32_window
+ * vo_screenwidth
+ * vo_screenheight
+ *
+ * \return 1 = Success, 0 = Failure
+ */
+int vo_w32_init(struct vo *vo)
+{
+ struct vo_w32_state *w32 = vo->w32;
+ if (w32 && w32->window)
+ return 1;
+
+ if (!w32)
+ w32 = vo->w32 = talloc_zero(vo, struct vo_w32_state);
+
+ HINSTANCE hInstance = GetModuleHandleW(NULL);
+
+ HICON mplayerIcon = LoadIconW(hInstance, L"IDI_ICON1");
+
+ WNDCLASSEXW wcex = {
+ .cbSize = sizeof wcex,
+ .style = CS_OWNDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW,
+ .lpfnWndProc = WndProc,
+ .hInstance = hInstance,
+ .hIcon = mplayerIcon,
+ .hCursor = LoadCursor(0, IDC_ARROW),
+ .lpszClassName = classname,
+ .hIconSm = mplayerIcon,
+ };
+
+ if (!RegisterClassExW(&wcex)) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "vo: win32: unable to register window class!\n");
+ return 0;
+ }
+
+ if (WinID >= 0) {
+ RECT r;
+ GetClientRect(WIN_ID_TO_HWND(WinID), &r);
+ vo->dwidth = r.right; vo->dheight = r.bottom;
+ w32->window = CreateWindowExW(WS_EX_NOPARENTNOTIFY, classname,
+ classname,
+ WS_CHILD | WS_VISIBLE,
+ 0, 0, vo->dwidth, vo->dheight,
+ WIN_ID_TO_HWND(WinID), 0, hInstance, vo);
+ } else {
+ w32->window = CreateWindowExW(0, classname,
+ classname,
+ update_style(vo, 0),
+ CW_USEDEFAULT, 0, 100, 100,
+ 0, 0, hInstance, vo);
+ }
+
+ if (!w32->window) {
+ mp_msg(MSGT_VO, MSGL_ERR, "vo: win32: unable to create window!\n");
+ return 0;
+ }
+
+ if (WinID >= 0)
+ EnableWindow(w32->window, 0);
+
+ updateScreenProperties(vo);
+
+ mp_msg(MSGT_VO, MSGL_V, "vo: win32: running at %dx%d with depth %d\n",
+ vo->opts->vo_screenwidth, vo->opts->vo_screenheight,
+ w32->depthonscreen);
+
+ return 1;
+}
+
+/**
+ * \brief Toogle fullscreen / windowed mode.
+ *
+ * Should be called on VOCTRL_FULLSCREEN event. The window is
+ * always resized during this call, so the rendering context
+ * should be reinitialized with the new dimensions.
+ * It is unspecified if vo_check_events will create a resize
+ * event in addition or not.
+ */
+
+void vo_w32_fullscreen(struct vo *vo)
+{
+ vo_fs = !vo_fs;
+ reinit_window_state(vo);
+}
+
+/**
+ * \brief Toogle window border attribute.
+ *
+ * Should be called on VOCTRL_BORDER event.
+ */
+void vo_w32_border(struct vo *vo)
+{
+ vo_border = !vo_border;
+ reinit_window_state(vo);
+}
+
+/**
+ * \brief Toogle window ontop attribute.
+ *
+ * Should be called on VOCTRL_ONTOP event.
+ */
+void vo_w32_ontop(struct vo *vo)
+{
+ vo->opts->vo_ontop = !vo->opts->vo_ontop;
+ reinit_window_state(vo);
+}
+
+/**
+ * \brief Uninitialize w32_common framework.
+ *
+ * Should be called last in video driver's uninit function. First release
+ * anything built on top of the created window e.g. rendering context inside
+ * and call vo_w32_uninit at the end.
+ */
+void vo_w32_uninit(struct vo *vo)
+{
+ struct vo_w32_state *w32 = vo->w32;
+ mp_msg(MSGT_VO, MSGL_V, "vo: win32: uninit\n");
+ if (!w32)
+ return;
+ resetMode(vo);
+ ShowCursor(1);
+ DestroyWindow(w32->window);
+ UnregisterClassW(classname, 0);
+ talloc_free(w32);
+ vo->w32 = NULL;
+}
+
+/**
+ * \brief get a device context to draw in
+ *
+ * \param wnd window the DC should belong to if it makes sense
+ */
+HDC vo_w32_get_dc(struct vo *vo, HWND wnd)
+{
+ struct vo_w32_state *w32 = vo->w32;
+ return GetDC(wnd);
+}
+
+/**
+ * \brief release a device context
+ *
+ * \param wnd window the DC probably belongs to
+ */
+void vo_w32_release_dc(struct vo *vo, HWND wnd, HDC dc)
+{
+ struct vo_w32_state *w32 = vo->w32;
+ ReleaseDC(wnd, dc);
+}
diff --git a/video/out/w32_common.h b/video/out/w32_common.h
new file mode 100644
index 0000000000..c6d9fc7653
--- /dev/null
+++ b/video/out/w32_common.h
@@ -0,0 +1,66 @@
+/*
+ * 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_W32_COMMON_H
+#define MPLAYER_W32_COMMON_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <windows.h>
+
+struct vo_w32_state {
+ HWND window;
+
+ bool vm;
+
+ int depthonscreen;
+
+ // last non-fullscreen extends (updated only on fullscreen or on initialization)
+ int prev_width;
+ int prev_height;
+ int prev_x;
+ int prev_y;
+
+ // whether the window position and size were intialized
+ bool window_bounds_initialized;
+
+ bool current_fs;
+
+ int window_x;
+ int window_y;
+
+ // video size
+ uint32_t o_dwidth;
+ uint32_t o_dheight;
+
+ int event_flags;
+ int mon_cnt;
+};
+
+int vo_w32_init(struct vo *vo);
+void vo_w32_uninit(struct vo *vo);
+void vo_w32_ontop(struct vo *vo);
+void vo_w32_border(struct vo *vo);
+void vo_w32_fullscreen(struct vo *vo);
+int vo_w32_check_events(struct vo *vo);
+int vo_w32_config(struct vo *vo, uint32_t, uint32_t, uint32_t);
+void w32_update_xinerama_info(struct vo *vo);
+HDC vo_w32_get_dc(struct vo *vo, HWND wnd);
+void vo_w32_release_dc(struct vo *vo, HWND wnd, HDC dc);
+
+#endif /* MPLAYER_W32_COMMON_H */
diff --git a/video/out/x11_common.c b/video/out/x11_common.c
new file mode 100644
index 0000000000..04d5c6880b
--- /dev/null
+++ b/video/out/x11_common.c
@@ -0,0 +1,2404 @@
+/*
+ * 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 <math.h>
+#include <inttypes.h>
+#include <limits.h>
+
+#include "config.h"
+#include "bstr.h"
+#include "options.h"
+#include "mp_msg.h"
+#include "mp_fifo.h"
+#include "libavutil/common.h"
+#include "x11_common.h"
+#include "talloc.h"
+
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include "video_out.h"
+#include "aspect.h"
+#include "geometry.h"
+#include "osdep/timer.h"
+
+#include <X11/Xmd.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include <X11/keysym.h>
+
+#ifdef CONFIG_XSS
+#include <X11/extensions/scrnsaver.h>
+#endif
+
+#ifdef CONFIG_XDPMS
+#include <X11/extensions/dpms.h>
+#endif
+
+#ifdef CONFIG_XINERAMA
+#include <X11/extensions/Xinerama.h>
+#endif
+
+#ifdef CONFIG_XF86VM
+#include <X11/extensions/xf86vmode.h>
+#endif
+
+#ifdef CONFIG_XF86XK
+#include <X11/XF86keysym.h>
+#endif
+
+#ifdef CONFIG_XV
+#include <X11/extensions/Xv.h>
+#include <X11/extensions/Xvlib.h>
+
+#include "subopt-helper.h"
+#endif
+
+#include "input/input.h"
+#include "input/keycodes.h"
+
+#define WIN_LAYER_ONBOTTOM 2
+#define WIN_LAYER_NORMAL 4
+#define WIN_LAYER_ONTOP 6
+#define WIN_LAYER_ABOVE_DOCK 10
+
+int fs_layer = WIN_LAYER_ABOVE_DOCK;
+
+int stop_xscreensaver = 1;
+
+static int dpms_disabled = 0;
+
+char *mDisplayName = NULL;
+
+char **vo_fstype_list;
+
+/* 1 means that the WM is metacity (broken as hell) */
+int metacity_hack = 0;
+
+#ifdef CONFIG_XF86VM
+static int modecount;
+static XF86VidModeModeInfo **vidmodes;
+static XF86VidModeModeLine modeline;
+#endif
+
+static int vo_x11_get_fs_type(int supported);
+static void saver_off(Display *);
+static void saver_on(Display *);
+
+/*
+ * Sends the EWMH fullscreen state event.
+ *
+ * action: could be one of _NET_WM_STATE_REMOVE -- remove state
+ * _NET_WM_STATE_ADD -- add state
+ * _NET_WM_STATE_TOGGLE -- toggle
+ */
+void vo_x11_ewmh_fullscreen(struct vo_x11_state *x11, int action)
+{
+ assert(action == _NET_WM_STATE_REMOVE ||
+ action == _NET_WM_STATE_ADD || action == _NET_WM_STATE_TOGGLE);
+
+ if (x11->fs_type & vo_wm_FULLSCREEN)
+ {
+ XEvent xev;
+
+ /* init X event structure for _NET_WM_FULLSCREEN client message */
+ xev.xclient.type = ClientMessage;
+ xev.xclient.serial = 0;
+ xev.xclient.send_event = True;
+ xev.xclient.message_type = x11->XA_NET_WM_STATE;
+ xev.xclient.window = x11->window;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = action;
+ xev.xclient.data.l[1] = x11->XA_NET_WM_STATE_FULLSCREEN;
+ xev.xclient.data.l[2] = 0;
+ xev.xclient.data.l[3] = 0;
+ xev.xclient.data.l[4] = 0;
+
+ /* finally send that damn thing */
+ if (!XSendEvent(x11->display, DefaultRootWindow(x11->display), False,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ &xev))
+ {
+ mp_tmsg(MSGT_VO, MSGL_ERR, "\nX11: Couldn't send EWMH fullscreen event!\n");
+ }
+ }
+}
+
+static void vo_hidecursor(Display * disp, Window win)
+{
+ Cursor no_ptr;
+ Pixmap bm_no;
+ XColor black, dummy;
+ Colormap colormap;
+ const char bm_no_data[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ if (WinID == 0)
+ return; // do not hide if playing on the root window
+
+ colormap = DefaultColormap(disp, DefaultScreen(disp));
+ if ( !XAllocNamedColor(disp, colormap, "black", &black, &dummy) )
+ {
+ return; // color alloc failed, give up
+ }
+ bm_no = XCreateBitmapFromData(disp, win, bm_no_data, 8, 8);
+ no_ptr = XCreatePixmapCursor(disp, bm_no, bm_no, &black, &black, 0, 0);
+ XDefineCursor(disp, win, no_ptr);
+ XFreeCursor(disp, no_ptr);
+ if (bm_no != None)
+ XFreePixmap(disp, bm_no);
+ XFreeColors(disp,colormap,&black.pixel,1,0);
+}
+
+static void vo_showcursor(Display * disp, Window win)
+{
+ if (WinID == 0)
+ return;
+ XDefineCursor(disp, win, 0);
+}
+
+static int x11_errorhandler(Display * display, XErrorEvent * event)
+{
+#define MSGLEN 60
+ char msg[MSGLEN];
+
+ XGetErrorText(display, event->error_code, (char *) &msg, MSGLEN);
+
+ mp_msg(MSGT_VO, MSGL_ERR, "X11 error: %s\n", msg);
+
+ mp_msg(MSGT_VO, MSGL_V,
+ "Type: %x, display: %p, resourceid: %lx, serial: %lx\n",
+ event->type, event->display, event->resourceid, event->serial);
+ mp_msg(MSGT_VO, MSGL_V,
+ "Error code: %x, request code: %x, minor code: %x\n",
+ event->error_code, event->request_code, event->minor_code);
+
+// abort();
+ return 0;
+#undef MSGLEN
+}
+
+void fstype_help(void)
+{
+ mp_tmsg(MSGT_VO, MSGL_INFO, "Available fullscreen layer change modes:\n");
+ mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_FULL_SCREEN_TYPES\n");
+
+ mp_msg(MSGT_VO, MSGL_INFO, " %-15s %s\n", "none",
+ "don't set fullscreen window layer");
+ mp_msg(MSGT_VO, MSGL_INFO, " %-15s %s\n", "layer",
+ "use _WIN_LAYER hint with default layer");
+ mp_msg(MSGT_VO, MSGL_INFO, " %-15s %s\n", "layer=<0..15>",
+ "use _WIN_LAYER hint with a given layer number");
+ mp_msg(MSGT_VO, MSGL_INFO, " %-15s %s\n", "netwm",
+ "force NETWM style");
+ mp_msg(MSGT_VO, MSGL_INFO, " %-15s %s\n", "above",
+ "use _NETWM_STATE_ABOVE hint if available");
+ mp_msg(MSGT_VO, MSGL_INFO, " %-15s %s\n", "below",
+ "use _NETWM_STATE_BELOW hint if available");
+ mp_msg(MSGT_VO, MSGL_INFO, " %-15s %s\n", "fullscreen",
+ "use _NETWM_STATE_FULLSCREEN hint if available");
+ mp_msg(MSGT_VO, MSGL_INFO, " %-15s %s\n", "stays_on_top",
+ "use _NETWM_STATE_STAYS_ON_TOP hint if available");
+ mp_msg(MSGT_VO, MSGL_INFO,
+ "You can also negate the settings with simply putting '-' in the beginning");
+ mp_msg(MSGT_VO, MSGL_INFO, "\n");
+}
+
+static void fstype_dump(int fstype)
+{
+ if (fstype)
+ {
+ mp_msg(MSGT_VO, MSGL_V, "[x11] Current fstype setting honours");
+ if (fstype & vo_wm_LAYER)
+ mp_msg(MSGT_VO, MSGL_V, " LAYER");
+ if (fstype & vo_wm_FULLSCREEN)
+ mp_msg(MSGT_VO, MSGL_V, " FULLSCREEN");
+ if (fstype & vo_wm_STAYS_ON_TOP)
+ mp_msg(MSGT_VO, MSGL_V, " STAYS_ON_TOP");
+ if (fstype & vo_wm_ABOVE)
+ mp_msg(MSGT_VO, MSGL_V, " ABOVE");
+ if (fstype & vo_wm_BELOW)
+ mp_msg(MSGT_VO, MSGL_V, " BELOW");
+ mp_msg(MSGT_VO, MSGL_V, " X atoms\n");
+ } else
+ mp_msg(MSGT_VO, MSGL_V,
+ "[x11] Current fstype setting doesn't honour any X atoms\n");
+}
+
+static int net_wm_support_state_test(struct vo_x11_state *x11, Atom atom)
+{
+#define NET_WM_STATE_TEST(x) { if (atom == x11->XA_NET_WM_STATE_##x) { mp_msg( MSGT_VO,MSGL_V, "[x11] Detected wm supports " #x " state.\n" ); return vo_wm_##x; } }
+
+ NET_WM_STATE_TEST(FULLSCREEN);
+ NET_WM_STATE_TEST(ABOVE);
+ NET_WM_STATE_TEST(STAYS_ON_TOP);
+ NET_WM_STATE_TEST(BELOW);
+ return 0;
+}
+
+static int x11_get_property(struct vo_x11_state *x11, Atom type, Atom ** args,
+ unsigned long *nitems)
+{
+ int format;
+ unsigned long bytesafter;
+
+ return Success ==
+ XGetWindowProperty(x11->display, x11->rootwin, type, 0, 16384, False,
+ AnyPropertyType, &type, &format, nitems,
+ &bytesafter, (unsigned char **) args)
+ && *nitems > 0;
+}
+
+static int vo_wm_detect(struct vo *vo)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ int i;
+ int wm = 0;
+ unsigned long nitems;
+ Atom *args = NULL;
+
+ if (WinID >= 0)
+ return 0;
+
+// -- supports layers
+ if (x11_get_property(x11, x11->XA_WIN_PROTOCOLS, &args, &nitems))
+ {
+ mp_msg(MSGT_VO, MSGL_V, "[x11] Detected wm supports layers.\n");
+ for (i = 0; i < nitems; i++)
+ {
+ if (args[i] == x11->XA_WIN_LAYER)
+ {
+ wm |= vo_wm_LAYER;
+ metacity_hack |= 1;
+ } else
+ /* metacity is the only window manager I know which reports
+ * supporting only the _WIN_LAYER hint in _WIN_PROTOCOLS.
+ * (what's more support for it is broken) */
+ metacity_hack |= 2;
+ }
+ XFree(args);
+ if (wm && (metacity_hack == 1))
+ {
+ // metacity claims to support layers, but it is not the truth :-)
+ wm ^= vo_wm_LAYER;
+ mp_msg(MSGT_VO, MSGL_V,
+ "[x11] Using workaround for Metacity bugs.\n");
+ }
+ }
+// --- netwm
+ if (x11_get_property(x11, x11->XA_NET_SUPPORTED, &args, &nitems))
+ {
+ mp_msg(MSGT_VO, MSGL_V, "[x11] Detected wm supports NetWM.\n");
+ for (i = 0; i < nitems; i++)
+ wm |= net_wm_support_state_test(vo->x11, args[i]);
+ XFree(args);
+ }
+
+ if (wm == 0)
+ mp_msg(MSGT_VO, MSGL_V, "[x11] Unknown wm type...\n");
+ return wm;
+}
+
+#define XA_INIT(x) x11->XA##x = XInternAtom(x11->display, #x, False)
+static void init_atoms(struct vo_x11_state *x11)
+{
+ XA_INIT(_NET_SUPPORTED);
+ XA_INIT(_NET_WM_STATE);
+ XA_INIT(_NET_WM_STATE_FULLSCREEN);
+ XA_INIT(_NET_WM_STATE_ABOVE);
+ XA_INIT(_NET_WM_STATE_STAYS_ON_TOP);
+ XA_INIT(_NET_WM_STATE_BELOW);
+ XA_INIT(_NET_WM_PID);
+ XA_INIT(_NET_WM_NAME);
+ XA_INIT(_NET_WM_ICON_NAME);
+ XA_INIT(_WIN_PROTOCOLS);
+ XA_INIT(_WIN_LAYER);
+ XA_INIT(_WIN_HINTS);
+ XA_INIT(WM_PROTOCOLS);
+ XA_INIT(WM_DELETE_WINDOW);
+ XA_INIT(UTF8_STRING);
+ char buf[50];
+ sprintf(buf, "_NET_WM_CM_S%d", x11->screen);
+ x11->XA_NET_WM_CM = XInternAtom(x11->display, buf, False);
+}
+
+void update_xinerama_info(struct vo *vo) {
+ struct MPOpts *opts = vo->opts;
+ xinerama_x = xinerama_y = 0;
+#ifdef CONFIG_XINERAMA
+ if (xinerama_screen >= -1 && XineramaIsActive(vo->x11->display))
+ {
+ int screen = xinerama_screen;
+ XineramaScreenInfo *screens;
+ int num_screens;
+
+ screens = XineramaQueryScreens(vo->x11->display, &num_screens);
+ if (screen >= num_screens)
+ screen = num_screens - 1;
+ if (screen == -1) {
+ int x = vo->dx + vo->dwidth / 2;
+ int y = vo->dy + vo->dheight / 2;
+ for (screen = num_screens - 1; screen > 0; screen--) {
+ int left = screens[screen].x_org;
+ int right = left + screens[screen].width;
+ int top = screens[screen].y_org;
+ int bottom = top + screens[screen].height;
+ if (left <= x && x <= right && top <= y && y <= bottom)
+ break;
+ }
+ }
+ if (screen < 0)
+ screen = 0;
+ opts->vo_screenwidth = screens[screen].width;
+ opts->vo_screenheight = screens[screen].height;
+ xinerama_x = screens[screen].x_org;
+ xinerama_y = screens[screen].y_org;
+
+ XFree(screens);
+ }
+#endif
+ aspect_save_screenres(vo, opts->vo_screenwidth, opts->vo_screenheight);
+}
+
+int vo_init(struct vo *vo)
+{
+ struct MPOpts *opts = vo->opts;
+// int mScreen;
+ int depth, bpp;
+ unsigned int mask;
+
+// char * DisplayName = ":0.0";
+// Display * mDisplay;
+ XImage *mXImage = NULL;
+
+// Window mRootWin;
+ XWindowAttributes attribs;
+ char *dispName;
+
+ if (vo->x11)
+ return 1;
+
+ vo->x11 = vo_x11_init_state();
+ struct vo_x11_state *x11 = vo->x11;
+
+ if (vo_rootwin)
+ WinID = 0; // use root window
+
+ if (x11->depthonscreen)
+ {
+ saver_off(x11->display);
+ return 1; // already called
+ }
+
+ XSetErrorHandler(x11_errorhandler);
+
+#if 0
+ if (!mDisplayName)
+ if (!(mDisplayName = getenv("DISPLAY")))
+ mDisplayName = strdup(":0.0");
+#else
+ dispName = XDisplayName(mDisplayName);
+#endif
+
+ mp_msg(MSGT_VO, MSGL_V, "X11 opening display: %s\n", dispName);
+
+ x11->display = XOpenDisplay(dispName);
+ if (!x11->display)
+ {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "vo: couldn't open the X11 display (%s)!\n", dispName);
+ talloc_free(x11);
+ vo->x11 = NULL;
+ return 0;
+ }
+ x11->screen = DefaultScreen(x11->display); // screen ID
+ x11->rootwin = RootWindow(x11->display, x11->screen); // root window ID
+
+ x11->xim = XOpenIM(x11->display, NULL, NULL, NULL);
+
+ init_atoms(vo->x11);
+
+#ifdef CONFIG_XF86VM
+ {
+ int clock;
+
+ XF86VidModeGetModeLine(x11->display, x11->screen, &clock, &modeline);
+ if (!opts->vo_screenwidth)
+ opts->vo_screenwidth = modeline.hdisplay;
+ if (!opts->vo_screenheight)
+ opts->vo_screenheight = modeline.vdisplay;
+ }
+#endif
+ {
+ if (!opts->vo_screenwidth)
+ opts->vo_screenwidth = DisplayWidth(x11->display, x11->screen);
+ if (!opts->vo_screenheight)
+ opts->vo_screenheight = DisplayHeight(x11->display, x11->screen);
+ }
+ // get color depth (from root window, or the best visual):
+ XGetWindowAttributes(x11->display, x11->rootwin, &attribs);
+ depth = attribs.depth;
+
+ if (depth != 15 && depth != 16 && depth != 24 && depth != 32)
+ {
+ Visual *visual;
+
+ depth = vo_find_depth_from_visuals(x11->display, x11->screen, &visual);
+ if (depth != -1)
+ mXImage = XCreateImage(x11->display, visual, depth, ZPixmap,
+ 0, NULL, 1, 1, 8, 1);
+ } else
+ mXImage =
+ XGetImage(x11->display, x11->rootwin, 0, 0, 1, 1, AllPlanes, ZPixmap);
+
+ x11->depthonscreen = depth; // display depth on screen
+
+ // get bits/pixel from XImage structure:
+ if (mXImage == NULL)
+ {
+ mask = 0;
+ } else
+ {
+ /*
+ * for the depth==24 case, the XImage structures might use
+ * 24 or 32 bits of data per pixel. The x11->depthonscreen
+ * field stores the amount of data per pixel in the
+ * XImage structure!
+ *
+ * Maybe we should rename vo_depthonscreen to (or add) vo_bpp?
+ */
+ bpp = mXImage->bits_per_pixel;
+ if ((x11->depthonscreen + 7) / 8 != (bpp + 7) / 8)
+ x11->depthonscreen = bpp; // by A'rpi
+ mask =
+ mXImage->red_mask | mXImage->green_mask | mXImage->blue_mask;
+ mp_msg(MSGT_VO, MSGL_V,
+ "vo: X11 color mask: %X (R:%lX G:%lX B:%lX)\n", mask,
+ mXImage->red_mask, mXImage->green_mask, mXImage->blue_mask);
+ XDestroyImage(mXImage);
+ }
+ if (((x11->depthonscreen + 7) / 8) == 2)
+ {
+ if (mask == 0x7FFF)
+ x11->depthonscreen = 15;
+ else if (mask == 0xFFFF)
+ x11->depthonscreen = 16;
+ }
+// XCloseDisplay( mDisplay );
+/* slightly improved local display detection AST */
+ if (strncmp(dispName, "unix:", 5) == 0)
+ dispName += 4;
+ else if (strncmp(dispName, "localhost:", 10) == 0)
+ dispName += 9;
+ if (*dispName == ':' && atoi(dispName + 1) < 10)
+ x11->display_is_local = 1;
+ else
+ x11->display_is_local = 0;
+ mp_msg(MSGT_VO, MSGL_V,
+ "vo: X11 running at %dx%d with depth %d and %d bpp (\"%s\" => %s display)\n",
+ opts->vo_screenwidth, opts->vo_screenheight, depth, x11->depthonscreen,
+ dispName, x11->display_is_local ? "local" : "remote");
+
+ x11->wm_type = vo_wm_detect(vo);
+
+ x11->fs_type = vo_x11_get_fs_type(x11->wm_type);
+
+ fstype_dump(x11->fs_type);
+
+ saver_off(x11->display);
+ return 1;
+}
+
+void vo_uninit(struct vo_x11_state *x11)
+{
+ if (!x11)
+ return;
+ if (!x11->display)
+ {
+ mp_msg(MSGT_VO, MSGL_V,
+ "vo: x11 uninit called but X11 not initialized..\n");
+ } else {
+ mp_msg(MSGT_VO, MSGL_V, "vo: uninit ...\n");
+ if (x11->xim)
+ XCloseIM(x11->xim);
+ XSetErrorHandler(NULL);
+ XCloseDisplay(x11->display);
+ x11->depthonscreen = 0;
+ x11->display = NULL;
+ }
+ talloc_free(x11);
+}
+
+static const struct mp_keymap keymap[] = {
+ // special keys
+ {XK_Pause, KEY_PAUSE}, {XK_Escape, KEY_ESC}, {XK_BackSpace, KEY_BS},
+ {XK_Tab, KEY_TAB}, {XK_Return, KEY_ENTER},
+ {XK_Menu, KEY_MENU}, {XK_Print, KEY_PRINT},
+
+ // cursor keys
+ {XK_Left, KEY_LEFT}, {XK_Right, KEY_RIGHT}, {XK_Up, KEY_UP}, {XK_Down, KEY_DOWN},
+
+ // navigation block
+ {XK_Insert, KEY_INSERT}, {XK_Delete, KEY_DELETE}, {XK_Home, KEY_HOME}, {XK_End, KEY_END},
+ {XK_Page_Up, KEY_PAGE_UP}, {XK_Page_Down, KEY_PAGE_DOWN},
+
+ // F-keys
+ {XK_F1, KEY_F+1}, {XK_F2, KEY_F+2}, {XK_F3, KEY_F+3}, {XK_F4, KEY_F+4},
+ {XK_F5, KEY_F+5}, {XK_F6, KEY_F+6}, {XK_F7, KEY_F+7}, {XK_F8, KEY_F+8},
+ {XK_F9, KEY_F+9}, {XK_F10, KEY_F+10}, {XK_F11, KEY_F+11}, {XK_F12, KEY_F+12},
+
+ // numpad independent of numlock
+ {XK_KP_Subtract, '-'}, {XK_KP_Add, '+'}, {XK_KP_Multiply, '*'}, {XK_KP_Divide, '/'},
+ {XK_KP_Enter, KEY_KPENTER},
+
+ // numpad with numlock
+ {XK_KP_0, KEY_KP0}, {XK_KP_1, KEY_KP1}, {XK_KP_2, KEY_KP2},
+ {XK_KP_3, KEY_KP3}, {XK_KP_4, KEY_KP4}, {XK_KP_5, KEY_KP5},
+ {XK_KP_6, KEY_KP6}, {XK_KP_7, KEY_KP7}, {XK_KP_8, KEY_KP8},
+ {XK_KP_9, KEY_KP9}, {XK_KP_Decimal, KEY_KPDEC},
+ {XK_KP_Separator, KEY_KPDEC},
+
+ // numpad without numlock
+ {XK_KP_Insert, KEY_KPINS}, {XK_KP_End, KEY_KP1}, {XK_KP_Down, KEY_KP2},
+ {XK_KP_Page_Down, KEY_KP3}, {XK_KP_Left, KEY_KP4}, {XK_KP_Begin, KEY_KP5},
+ {XK_KP_Right, KEY_KP6}, {XK_KP_Home, KEY_KP7}, {XK_KP_Up, KEY_KP8},
+ {XK_KP_Page_Up, KEY_KP9}, {XK_KP_Delete, KEY_KPDEL},
+
+#ifdef XF86XK_AudioPause
+ {XF86XK_MenuKB, KEY_MENU},
+ {XF86XK_AudioPlay, KEY_PLAY}, {XF86XK_AudioPause, KEY_PAUSE}, {XF86XK_AudioStop, KEY_STOP},
+ {XF86XK_AudioPrev, KEY_PREV}, {XF86XK_AudioNext, KEY_NEXT},
+ {XF86XK_AudioMute, KEY_MUTE}, {XF86XK_AudioLowerVolume, KEY_VOLUME_DOWN}, {XF86XK_AudioRaiseVolume, KEY_VOLUME_UP},
+#endif
+
+ {0, 0}
+};
+
+static int vo_x11_lookupkey(int key)
+{
+ static const char *passthrough_keys = " -+*/<>`~!@#$%^&()_{}:;\"\',.?\\|=[]";
+ int mpkey = 0;
+ if ((key >= 'a' && key <= 'z') ||
+ (key >= 'A' && key <= 'Z') ||
+ (key >= '0' && key <= '9') ||
+ (key > 0 && key < 256 && strchr(passthrough_keys, key)))
+ mpkey = key;
+
+ if (!mpkey)
+ mpkey = lookup_keymap_table(keymap, key);
+
+ return mpkey;
+}
+
+
+// ----- Motif header: -------
+
+#define MWM_HINTS_FUNCTIONS (1L << 0)
+#define MWM_HINTS_DECORATIONS (1L << 1)
+#define MWM_HINTS_INPUT_MODE (1L << 2)
+#define MWM_HINTS_STATUS (1L << 3)
+
+#define MWM_FUNC_ALL (1L << 0)
+#define MWM_FUNC_RESIZE (1L << 1)
+#define MWM_FUNC_MOVE (1L << 2)
+#define MWM_FUNC_MINIMIZE (1L << 3)
+#define MWM_FUNC_MAXIMIZE (1L << 4)
+#define MWM_FUNC_CLOSE (1L << 5)
+
+#define MWM_DECOR_ALL (1L << 0)
+#define MWM_DECOR_BORDER (1L << 1)
+#define MWM_DECOR_RESIZEH (1L << 2)
+#define MWM_DECOR_TITLE (1L << 3)
+#define MWM_DECOR_MENU (1L << 4)
+#define MWM_DECOR_MINIMIZE (1L << 5)
+#define MWM_DECOR_MAXIMIZE (1L << 6)
+
+#define MWM_INPUT_MODELESS 0
+#define MWM_INPUT_PRIMARY_APPLICATION_MODAL 1
+#define MWM_INPUT_SYSTEM_MODAL 2
+#define MWM_INPUT_FULL_APPLICATION_MODAL 3
+#define MWM_INPUT_APPLICATION_MODAL MWM_INPUT_PRIMARY_APPLICATION_MODAL
+
+#define MWM_TEAROFF_WINDOW (1L<<0)
+
+typedef struct
+{
+ long flags;
+ long functions;
+ long decorations;
+ long input_mode;
+ long state;
+} MotifWmHints;
+
+static MotifWmHints vo_MotifWmHints;
+static Atom vo_MotifHints = None;
+
+void vo_x11_decoration(struct vo *vo, int d)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ Atom mtype;
+ int mformat;
+ unsigned long mn, mb;
+
+ if (!WinID)
+ return;
+
+ if (vo_fsmode & 8)
+ {
+ XSetTransientForHint(x11->display, x11->window,
+ RootWindow(x11->display, x11->screen));
+ }
+
+ vo_MotifHints = XInternAtom(x11->display, "_MOTIF_WM_HINTS", 0);
+ if (vo_MotifHints != None)
+ {
+ if (!d)
+ {
+ MotifWmHints *mhints = NULL;
+
+ XGetWindowProperty(x11->display, x11->window,
+ vo_MotifHints, 0, 20, False,
+ vo_MotifHints, &mtype, &mformat, &mn,
+ &mb, (unsigned char **) &mhints);
+ if (mhints)
+ {
+ if (mhints->flags & MWM_HINTS_DECORATIONS)
+ x11->olddecor = mhints->decorations;
+ if (mhints->flags & MWM_HINTS_FUNCTIONS)
+ x11->oldfuncs = mhints->functions;
+ XFree(mhints);
+ }
+ }
+
+ memset(&vo_MotifWmHints, 0, sizeof(MotifWmHints));
+ vo_MotifWmHints.flags =
+ MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS;
+ if (d)
+ {
+ vo_MotifWmHints.functions = x11->oldfuncs;
+ d = x11->olddecor;
+ }
+#if 0
+ vo_MotifWmHints.decorations =
+ d | ((vo_fsmode & 2) ? 0 : MWM_DECOR_MENU);
+#else
+ vo_MotifWmHints.decorations =
+ d | ((vo_fsmode & 2) ? MWM_DECOR_MENU : 0);
+#endif
+ XChangeProperty(x11->display, x11->window, vo_MotifHints,
+ vo_MotifHints, 32,
+ PropModeReplace,
+ (unsigned char *) &vo_MotifWmHints,
+ (vo_fsmode & 4) ? 4 : 5);
+ }
+}
+
+void vo_x11_classhint(struct vo *vo, Window window, const char *name)
+{
+ struct MPOpts *opts = vo->opts;
+ struct vo_x11_state *x11 = vo->x11;
+ XClassHint wmClass;
+ pid_t pid = getpid();
+
+ wmClass.res_name = opts->vo_winname ? opts->vo_winname : (char *)name;
+ wmClass.res_class = "mpv";
+ XSetClassHint(x11->display, window, &wmClass);
+ XChangeProperty(x11->display, window, x11->XA_NET_WM_PID, XA_CARDINAL,
+ 32, PropModeReplace, (unsigned char *) &pid, 1);
+}
+
+void vo_x11_uninit(struct vo *vo)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ saver_on(x11->display);
+ if (x11->window != None)
+ vo_showcursor(x11->display, x11->window);
+
+ if (x11->f_gc != None)
+ {
+ XFreeGC(vo->x11->display, x11->f_gc);
+ x11->f_gc = None;
+ }
+ {
+ if (x11->vo_gc != None)
+ {
+ XFreeGC(vo->x11->display, x11->vo_gc);
+ x11->vo_gc = None;
+ }
+ if (x11->window != None)
+ {
+ XClearWindow(x11->display, x11->window);
+ if (WinID < 0)
+ {
+ XEvent xev;
+
+ if (x11->xic)
+ XDestroyIC(x11->xic);
+ x11->xic = NULL;
+
+ XUnmapWindow(x11->display, x11->window);
+ XSelectInput(x11->display, x11->window, StructureNotifyMask);
+ XDestroyWindow(x11->display, x11->window);
+ do
+ {
+ XNextEvent(x11->display, &xev);
+ }
+ while (xev.type != DestroyNotify
+ || xev.xdestroywindow.event != x11->window);
+ }
+ x11->window = None;
+ }
+ vo_fs = 0;
+ x11->vo_old_width = x11->vo_old_height = 0;
+ x11->last_video_width = 0;
+ x11->last_video_height = 0;
+ x11->size_changed_during_fs = false;
+ }
+ vo_uninit(x11);
+ vo->x11 = NULL;
+}
+
+static int check_resize(struct vo *vo)
+{
+ int old_w = vo->dwidth, old_h = vo->dheight;
+ int old_x = vo->dx, old_y = vo->dy;
+ int rc = 0;
+ vo_x11_update_geometry(vo, true);
+ if (vo->dwidth != old_w || vo->dheight != old_h)
+ rc |= VO_EVENT_RESIZE;
+ if (vo->dx != old_x || vo->dy != old_y)
+ rc |= VO_EVENT_MOVE;
+ return rc;
+}
+
+int vo_x11_check_events(struct vo *vo)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ struct MPOpts *opts = vo->opts;
+ Display *display = vo->x11->display;
+ int ret = 0;
+ XEvent Event;
+
+ if (x11->mouse_waiting_hide && opts->cursor_autohide_delay != -1 &&
+ (GetTimerMS() - x11->mouse_timer >= opts->cursor_autohide_delay)) {
+ vo_hidecursor(display, x11->window);
+ x11->mouse_waiting_hide = 0;
+ }
+
+ if (WinID > 0)
+ ret |= check_resize(vo);
+ while (XPending(display))
+ {
+ XNextEvent(display, &Event);
+// printf("\rEvent.type=%X \n",Event.type);
+ switch (Event.type)
+ {
+ case Expose:
+ ret |= VO_EVENT_EXPOSE;
+ break;
+ case ConfigureNotify:
+ if (x11->window == None)
+ break;
+ ret |= check_resize(vo);
+ break;
+ case KeyPress:
+ {
+ char buf[100];
+ KeySym keySym = 0;
+ int modifiers = 0;
+ if (Event.xkey.state & ShiftMask)
+ modifiers |= KEY_MODIFIER_SHIFT;
+ if (Event.xkey.state & ControlMask)
+ modifiers |= KEY_MODIFIER_CTRL;
+ if (Event.xkey.state & Mod1Mask)
+ modifiers |= KEY_MODIFIER_ALT;
+ if (Event.xkey.state & Mod4Mask)
+ modifiers |= KEY_MODIFIER_META;
+ if (x11->xic) {
+ Status status;
+ int len = Xutf8LookupString(x11->xic, &Event.xkey, buf,
+ sizeof(buf), &keySym,
+ &status);
+ int mpkey = vo_x11_lookupkey(keySym);
+ if (mpkey) {
+ mplayer_put_key(vo->key_fifo, mpkey | modifiers);
+ } else if (status == XLookupChars
+ || status == XLookupBoth)
+ {
+ struct bstr t = { buf, len };
+ mplayer_put_key_utf8(vo->key_fifo, modifiers, t);
+ }
+ } else {
+ XLookupString(&Event.xkey, buf, sizeof(buf), &keySym,
+ &x11->compose_status);
+ int mpkey = vo_x11_lookupkey(keySym);
+ if (mpkey)
+ mplayer_put_key(vo->key_fifo, mpkey | modifiers);
+ }
+ ret |= VO_EVENT_KEYPRESS;
+ }
+ break;
+ case MotionNotify:
+ vo_mouse_movement(vo, Event.xmotion.x, Event.xmotion.y);
+
+ if (opts->cursor_autohide_delay > -2) {
+ vo_showcursor(display, x11->window);
+ x11->mouse_waiting_hide = 1;
+ x11->mouse_timer = GetTimerMS();
+ }
+ break;
+ case ButtonPress:
+ if (opts->cursor_autohide_delay > -2) {
+ vo_showcursor(display, x11->window);
+ x11->mouse_waiting_hide = 1;
+ x11->mouse_timer = GetTimerMS();
+ }
+ mplayer_put_key(vo->key_fifo,
+ (MOUSE_BTN0 + Event.xbutton.button - 1)
+ | MP_KEY_DOWN);
+ break;
+ case ButtonRelease:
+ if (opts->cursor_autohide_delay > -2) {
+ vo_showcursor(display, x11->window);
+ x11->mouse_waiting_hide = 1;
+ x11->mouse_timer = GetTimerMS();
+ }
+ mplayer_put_key(vo->key_fifo,
+ MOUSE_BTN0 + Event.xbutton.button - 1);
+ break;
+ case PropertyNotify:
+ {
+ char *name =
+ XGetAtomName(display, Event.xproperty.atom);
+
+ if (!name)
+ break;
+
+// fprintf(stderr,"[ws] PropertyNotify ( 0x%x ) %s ( 0x%x )\n",vo_window,name,Event.xproperty.atom );
+
+ XFree(name);
+ }
+ break;
+ case MapNotify:
+ x11->vo_hint.win_gravity = x11->old_gravity;
+ XSetWMNormalHints(display, x11->window, &x11->vo_hint);
+ x11->fs_flip = 0;
+ break;
+ case DestroyNotify:
+ mp_msg(MSGT_VO, MSGL_WARN, "Our window was destroyed, exiting\n");
+ mplayer_put_key(vo->key_fifo, KEY_CLOSE_WIN);
+ break;
+ case ClientMessage:
+ if (Event.xclient.message_type == x11->XAWM_PROTOCOLS &&
+ Event.xclient.data.l[0] == x11->XAWM_DELETE_WINDOW)
+ mplayer_put_key(vo->key_fifo, KEY_CLOSE_WIN);
+ break;
+ }
+ }
+ return ret;
+}
+
+/**
+ * \brief sets the size and position of the non-fullscreen window.
+ */
+static void vo_x11_nofs_sizepos(struct vo *vo, int x, int y,
+ int width, int height)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ if (width == x11->last_video_width && height == x11->last_video_height) {
+ if (!vo->opts->force_window_position && !x11->size_changed_during_fs)
+ return;
+ } else if (vo_fs)
+ x11->size_changed_during_fs = true;
+ x11->last_video_height = height;
+ x11->last_video_width = width;
+ vo_x11_sizehint(vo, x, y, width, height, 0);
+ if (vo_fs) {
+ x11->vo_old_x = x;
+ x11->vo_old_y = y;
+ x11->vo_old_width = width;
+ x11->vo_old_height = height;
+ }
+ else
+ {
+ vo->dwidth = width;
+ vo->dheight = height;
+ if (vo->opts->force_window_position)
+ XMoveResizeWindow(vo->x11->display, vo->x11->window, x, y, width,
+ height);
+ else
+ XResizeWindow(vo->x11->display, vo->x11->window, width, height);
+ }
+}
+
+void vo_x11_sizehint(struct vo *vo, int x, int y, int width, int height, int max)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ x11->vo_hint.flags = 0;
+ if (vo_keepaspect)
+ {
+ x11->vo_hint.flags |= PAspect;
+ x11->vo_hint.min_aspect.x = width;
+ x11->vo_hint.min_aspect.y = height;
+ x11->vo_hint.max_aspect.x = width;
+ x11->vo_hint.max_aspect.y = height;
+ }
+
+ x11->vo_hint.flags |= PPosition | PSize;
+ x11->vo_hint.x = x;
+ x11->vo_hint.y = y;
+ x11->vo_hint.width = width;
+ x11->vo_hint.height = height;
+ if (max)
+ {
+ x11->vo_hint.flags |= PMaxSize;
+ x11->vo_hint.max_width = width;
+ x11->vo_hint.max_height = height;
+ } else
+ {
+ x11->vo_hint.max_width = 0;
+ x11->vo_hint.max_height = 0;
+ }
+
+ // Set minimum height/width to 4 to avoid off-by-one errors.
+ x11->vo_hint.flags |= PMinSize;
+ x11->vo_hint.min_width = x11->vo_hint.min_height = 4;
+
+ // Set the base size. A window manager might display the window
+ // size to the user relative to this.
+ // Setting these to width/height might be nice, but e.g. fluxbox can't handle it.
+ x11->vo_hint.flags |= PBaseSize;
+ x11->vo_hint.base_width = 0 /*width*/;
+ x11->vo_hint.base_height = 0 /*height*/;
+
+ x11->vo_hint.flags |= PWinGravity;
+ x11->vo_hint.win_gravity = StaticGravity;
+ XSetWMNormalHints(x11->display, x11->window, &x11->vo_hint);
+}
+
+static int vo_x11_get_gnome_layer(struct vo_x11_state *x11, Window win)
+{
+ Atom type;
+ int format;
+ unsigned long nitems;
+ unsigned long bytesafter;
+ unsigned short *args = NULL;
+
+ if (XGetWindowProperty(x11->display, win, x11->XA_WIN_LAYER, 0, 16384,
+ False, AnyPropertyType, &type, &format, &nitems,
+ &bytesafter,
+ (unsigned char **) &args) == Success
+ && nitems > 0 && args)
+ {
+ mp_msg(MSGT_VO, MSGL_V, "[x11] original window layer is %d.\n",
+ *args);
+ return *args;
+ }
+ return WIN_LAYER_NORMAL;
+}
+
+// set a X text property that expects a UTF8_STRING type
+static void vo_x11_set_property_utf8(struct vo *vo, Atom name, const char *t)
+{
+ struct vo_x11_state *x11 = vo->x11;
+
+ XChangeProperty(x11->display, x11->window, name, x11->XAUTF8_STRING, 8,
+ PropModeReplace, t, strlen(t));
+}
+
+// set a X text property that expects a STRING or COMPOUND_TEXT type
+static void vo_x11_set_property_string(struct vo *vo, Atom name, const char *t)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ XTextProperty prop = {0};
+
+ if (Xutf8TextListToTextProperty(x11->display, (char **)&t, 1,
+ XStdICCTextStyle, &prop) == Success)
+ {
+ XSetTextProperty(x11->display, x11->window, &prop, name);
+ } else {
+ // Strictly speaking this violates the ICCCM, but there's no way we
+ // can do this correctly.
+ vo_x11_set_property_utf8(vo, name, t);
+ }
+
+ if (prop.value)
+ XFree(prop.value);
+}
+
+static void vo_x11_update_window_title(struct vo *vo)
+{
+ struct vo_x11_state *x11 = vo->x11;
+
+ const char *title = vo_get_window_title(vo);
+ vo_x11_set_property_string(vo, XA_WM_NAME, title);
+ vo_x11_set_property_string(vo, XA_WM_ICON_NAME, title);
+ vo_x11_set_property_utf8(vo, x11->XA_NET_WM_NAME, title);
+ vo_x11_set_property_utf8(vo, x11->XA_NET_WM_ICON_NAME, title);
+}
+
+//
+static Window vo_x11_create_smooth_window(struct vo_x11_state *x11, Window mRoot,
+ Visual * vis, int x, int y,
+ unsigned int width, unsigned int height,
+ int depth, Colormap col_map)
+{
+ unsigned long xswamask = CWBorderPixel;
+ XSetWindowAttributes xswa;
+ Window ret_win;
+
+ if (col_map != CopyFromParent)
+ {
+ xswa.colormap = col_map;
+ xswamask |= CWColormap;
+ }
+ xswa.background_pixel = 0;
+ xswa.border_pixel = 0;
+ xswa.backing_store = NotUseful;
+ xswa.bit_gravity = StaticGravity;
+
+ ret_win =
+ XCreateWindow(x11->display, x11->rootwin, x, y, width, height, 0, depth,
+ CopyFromParent, vis, xswamask, &xswa);
+ XSetWMProtocols(x11->display, ret_win, &x11->XAWM_DELETE_WINDOW, 1);
+ if (x11->f_gc == None)
+ x11->f_gc = XCreateGC(x11->display, ret_win, 0, 0);
+ XSetForeground(x11->display, x11->f_gc, 0);
+
+ return ret_win;
+}
+
+/**
+ * \brief create and setup a window suitable for display
+ * \param vis Visual to use for creating the window
+ * \param x x position of window
+ * \param y y position of window
+ * \param width width of window
+ * \param height height of window
+ * \param flags flags for window creation.
+ * Only VOFLAG_FULLSCREEN is supported so far.
+ * \param col_map Colourmap for window or CopyFromParent if a specific colormap isn't needed
+ * \param classname name to use for the classhint
+ *
+ * This also does the grunt-work like setting Window Manager hints etc.
+ * If vo_window is already set it just moves and resizes it.
+ */
+void vo_x11_create_vo_window(struct vo *vo, XVisualInfo *vis, int x, int y,
+ unsigned int width, unsigned int height, int flags,
+ Colormap col_map, const char *classname)
+{
+ struct MPOpts *opts = vo->opts;
+ struct vo_x11_state *x11 = vo->x11;
+ Display *mDisplay = vo->x11->display;
+ if (WinID >= 0) {
+ vo_fs = flags & VOFLAG_FULLSCREEN;
+ x11->window = WinID ? (Window)WinID : x11->rootwin;
+ if (col_map != CopyFromParent) {
+ unsigned long xswamask = CWColormap;
+ XSetWindowAttributes xswa;
+ xswa.colormap = col_map;
+ XChangeWindowAttributes(mDisplay, x11->window, xswamask, &xswa);
+ XInstallColormap(mDisplay, col_map);
+ }
+ if (WinID) {
+ // Expose events can only really be handled by us, so request them.
+ vo_x11_selectinput_witherr(mDisplay, x11->window, ExposureMask);
+ } else
+ // Do not capture events since it might break the parent application
+ // if it relies on events being forwarded to the parent of WinID.
+ // It also is consistent with the w32_common.c code.
+ vo_x11_selectinput_witherr(mDisplay, x11->window,
+ StructureNotifyMask | KeyPressMask | PointerMotionMask |
+ ButtonPressMask | ButtonReleaseMask | ExposureMask);
+
+ vo_x11_update_geometry(vo, true);
+ goto final;
+ }
+ if (x11->window == None) {
+ vo_fs = 0;
+ vo->dwidth = width;
+ vo->dheight = height;
+ x11->window = vo_x11_create_smooth_window(x11, x11->rootwin, vis->visual,
+ x, y, width, height, vis->depth, col_map);
+ x11->window_state = VOFLAG_HIDDEN;
+ }
+ if (flags & VOFLAG_HIDDEN)
+ goto final;
+ if (x11->window_state & VOFLAG_HIDDEN) {
+ XSizeHints hint;
+ x11->window_state &= ~VOFLAG_HIDDEN;
+ vo_x11_classhint(vo, x11->window, classname);
+ vo_hidecursor(mDisplay, x11->window);
+ XSelectInput(mDisplay, x11->window, StructureNotifyMask);
+ hint.x = x; hint.y = y;
+ hint.width = width; hint.height = height;
+ hint.flags = PSize;
+ if (geometry_xy_changed)
+ hint.flags |= PPosition;
+ XSetWMNormalHints(mDisplay, x11->window, &hint);
+ if (!vo_border) vo_x11_decoration(vo, 0);
+ // map window
+ x11->xic = XCreateIC(x11->xim,
+ XNInputStyle, XIMPreeditNone | XIMStatusNone,
+ XNClientWindow, x11->window,
+ XNFocusWindow, x11->window,
+ NULL);
+ XSelectInput(mDisplay, x11->window, NoEventMask);
+ vo_x11_selectinput_witherr(mDisplay, x11->window,
+ StructureNotifyMask | KeyPressMask | PointerMotionMask |
+ ButtonPressMask | ButtonReleaseMask | ExposureMask);
+ XMapWindow(mDisplay, x11->window);
+ vo_x11_clearwindow(vo, x11->window);
+ }
+ vo_x11_update_window_title(vo);
+ if (opts->vo_ontop) vo_x11_setlayer(vo, x11->window, opts->vo_ontop);
+ vo_x11_update_geometry(vo, !geometry_xy_changed);
+ vo_x11_nofs_sizepos(vo, vo->dx, vo->dy, width, height);
+ if (!!vo_fs != !!(flags & VOFLAG_FULLSCREEN))
+ vo_x11_fullscreen(vo);
+ else if (vo_fs) {
+ // if we are already in fullscreen do not switch back and forth, just
+ // set the size values right.
+ vo->dwidth = vo->opts->vo_screenwidth;
+ vo->dheight = vo->opts->vo_screenheight;
+ }
+final:
+ if (x11->vo_gc != None)
+ XFreeGC(mDisplay, x11->vo_gc);
+ x11->vo_gc = XCreateGC(mDisplay, x11->window, 0, NULL);
+
+ XSync(mDisplay, False);
+ vo->event_fd = ConnectionNumber(x11->display);
+}
+
+void vo_x11_clearwindow_part(struct vo *vo, Window vo_window,
+ int img_width, int img_height)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ Display *mDisplay = vo->x11->display;
+ int u_dheight, u_dwidth, left_ov, left_ov2;
+
+ if (x11->f_gc == None)
+ return;
+
+ u_dheight = vo->dheight;
+ u_dwidth = vo->dwidth;
+ if ((u_dheight <= img_height) && (u_dwidth <= img_width))
+ return;
+
+ left_ov = (u_dheight - img_height) / 2;
+ left_ov2 = (u_dwidth - img_width) / 2;
+
+ XFillRectangle(mDisplay, vo_window, x11->f_gc, 0, 0, u_dwidth, left_ov);
+ XFillRectangle(mDisplay, vo_window, x11->f_gc, 0, u_dheight - left_ov - 1,
+ u_dwidth, left_ov + 1);
+
+ if (u_dwidth > img_width)
+ {
+ XFillRectangle(mDisplay, vo_window, x11->f_gc, 0, left_ov, left_ov2,
+ img_height);
+ XFillRectangle(mDisplay, vo_window, x11->f_gc, u_dwidth - left_ov2 - 1,
+ left_ov, left_ov2 + 1, img_height);
+ }
+
+ XFlush(mDisplay);
+}
+
+void vo_x11_clearwindow(struct vo *vo, Window vo_window)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ struct MPOpts *opts = vo->opts;
+ if (x11->f_gc == None)
+ return;
+ XFillRectangle(x11->display, vo_window, x11->f_gc, 0, 0,
+ opts->vo_screenwidth, opts->vo_screenheight);
+ //
+ XFlush(x11->display);
+}
+
+
+void vo_x11_setlayer(struct vo *vo, Window vo_window, int layer)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ if (WinID >= 0)
+ return;
+
+ if (x11->fs_type & vo_wm_LAYER)
+ {
+ XClientMessageEvent xev;
+
+ if (!x11->orig_layer)
+ x11->orig_layer = vo_x11_get_gnome_layer(x11, vo_window);
+
+ memset(&xev, 0, sizeof(xev));
+ xev.type = ClientMessage;
+ xev.display = x11->display;
+ xev.window = vo_window;
+ xev.message_type = x11->XA_WIN_LAYER;
+ xev.format = 32;
+ xev.data.l[0] = layer ? fs_layer : x11->orig_layer; // if not fullscreen, stay on default layer
+ xev.data.l[1] = CurrentTime;
+ mp_msg(MSGT_VO, MSGL_V,
+ "[x11] Layered style stay on top (layer %ld).\n",
+ xev.data.l[0]);
+ XSendEvent(x11->display, x11->rootwin, False, SubstructureNotifyMask,
+ (XEvent *) & xev);
+ } else if (x11->fs_type & vo_wm_NETWM)
+ {
+ XClientMessageEvent xev;
+ char *state;
+
+ memset(&xev, 0, sizeof(xev));
+ xev.type = ClientMessage;
+ xev.message_type = x11->XA_NET_WM_STATE;
+ xev.display = x11->display;
+ xev.window = vo_window;
+ xev.format = 32;
+ xev.data.l[0] = layer;
+
+ if (x11->fs_type & vo_wm_STAYS_ON_TOP)
+ xev.data.l[1] = x11->XA_NET_WM_STATE_STAYS_ON_TOP;
+ else if (x11->fs_type & vo_wm_ABOVE)
+ xev.data.l[1] = x11->XA_NET_WM_STATE_ABOVE;
+ else if (x11->fs_type & vo_wm_FULLSCREEN)
+ xev.data.l[1] = x11->XA_NET_WM_STATE_FULLSCREEN;
+ else if (x11->fs_type & vo_wm_BELOW)
+ // This is not fallback. We can safely assume that the situation
+ // where only NETWM_STATE_BELOW is supported doesn't exist.
+ xev.data.l[1] = x11->XA_NET_WM_STATE_BELOW;
+
+ XSendEvent(x11->display, x11->rootwin, False, SubstructureRedirectMask,
+ (XEvent *) & xev);
+ state = XGetAtomName(x11->display, xev.data.l[1]);
+ mp_msg(MSGT_VO, MSGL_V,
+ "[x11] NET style stay on top (layer %d). Using state %s.\n",
+ layer, state);
+ XFree(state);
+ }
+}
+
+static int vo_x11_get_fs_type(int supported)
+{
+ int i;
+ int type = supported;
+
+ if (vo_fstype_list)
+ {
+ for (i = 0; vo_fstype_list[i]; i++)
+ {
+ int neg = 0;
+ char *arg = vo_fstype_list[i];
+
+ if (vo_fstype_list[i][0] == '-')
+ {
+ neg = 1;
+ arg = vo_fstype_list[i] + 1;
+ }
+
+ if (!strncmp(arg, "layer", 5))
+ {
+ if (!neg && (arg[5] == '='))
+ {
+ char *endptr = NULL;
+ int layer = strtol(vo_fstype_list[i] + 6, &endptr, 10);
+
+ if (endptr && *endptr == '\0' && layer >= 0
+ && layer <= 15)
+ fs_layer = layer;
+ }
+ if (neg)
+ type &= ~vo_wm_LAYER;
+ else
+ type |= vo_wm_LAYER;
+ } else if (!strcmp(arg, "above"))
+ {
+ if (neg)
+ type &= ~vo_wm_ABOVE;
+ else
+ type |= vo_wm_ABOVE;
+ } else if (!strcmp(arg, "fullscreen"))
+ {
+ if (neg)
+ type &= ~vo_wm_FULLSCREEN;
+ else
+ type |= vo_wm_FULLSCREEN;
+ } else if (!strcmp(arg, "stays_on_top"))
+ {
+ if (neg)
+ type &= ~vo_wm_STAYS_ON_TOP;
+ else
+ type |= vo_wm_STAYS_ON_TOP;
+ } else if (!strcmp(arg, "below"))
+ {
+ if (neg)
+ type &= ~vo_wm_BELOW;
+ else
+ type |= vo_wm_BELOW;
+ } else if (!strcmp(arg, "netwm"))
+ {
+ if (neg)
+ type &= ~vo_wm_NETWM;
+ else
+ type |= vo_wm_NETWM;
+ } else if (!strcmp(arg, "none"))
+ type = 0; // clear; keep parsing
+ }
+ }
+
+ return type;
+}
+
+/**
+ * \brief update vo->dx, vo->dy, vo->dwidth and vo->dheight with current values of vo->x11->window
+ * \return returns current color depth of vo->x11->window
+ */
+int vo_x11_update_geometry(struct vo *vo, bool update_pos)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ unsigned depth, w, h;
+ int dummy_int;
+ Window dummy_win;
+ XGetGeometry(x11->display, x11->window, &dummy_win, &dummy_int, &dummy_int,
+ &w, &h, &dummy_int, &depth);
+ if (w <= INT_MAX && h <= INT_MAX) {
+ vo->dwidth = w;
+ vo->dheight = h;
+ }
+ if (update_pos)
+ XTranslateCoordinates(x11->display, x11->window, x11->rootwin, 0, 0,
+ &vo->dx, &vo->dy, &dummy_win);
+
+ return depth <= INT_MAX ? depth : 0;
+}
+
+void vo_x11_fullscreen(struct vo *vo)
+{
+ struct MPOpts *opts = vo->opts;
+ struct vo_x11_state *x11 = vo->x11;
+ int x, y, w, h;
+ x = x11->vo_old_x;
+ y = x11->vo_old_y;
+ w = x11->vo_old_width;
+ h = x11->vo_old_height;
+
+ if (WinID >= 0) {
+ vo_fs = !vo_fs;
+ return;
+ }
+ if (x11->fs_flip)
+ return;
+
+ if (vo_fs)
+ {
+ vo_x11_ewmh_fullscreen(x11, _NET_WM_STATE_REMOVE); // removes fullscreen state if wm supports EWMH
+ vo_fs = VO_FALSE;
+ if (x11->size_changed_during_fs && (x11->fs_type & vo_wm_FULLSCREEN))
+ vo_x11_nofs_sizepos(vo, vo->dx, vo->dy, x11->last_video_width,
+ x11->last_video_height);
+ x11->size_changed_during_fs = false;
+ } else
+ {
+ // win->fs
+ vo_x11_ewmh_fullscreen(x11, _NET_WM_STATE_ADD); // sends fullscreen state to be added if wm supports EWMH
+
+ vo_fs = VO_TRUE;
+ if ( ! (x11->fs_type & vo_wm_FULLSCREEN) ) // not needed with EWMH fs
+ {
+ x11->vo_old_x = vo->dx;
+ x11->vo_old_y = vo->dy;
+ x11->vo_old_width = vo->dwidth;
+ x11->vo_old_height = vo->dheight;
+ }
+ update_xinerama_info(vo);
+ x = xinerama_x;
+ y = xinerama_y;
+ w = opts->vo_screenwidth;
+ h = opts->vo_screenheight;
+ }
+ {
+ long dummy;
+
+ XGetWMNormalHints(x11->display, x11->window, &x11->vo_hint, &dummy);
+ if (!(x11->vo_hint.flags & PWinGravity))
+ x11->old_gravity = NorthWestGravity;
+ else
+ x11->old_gravity = x11->vo_hint.win_gravity;
+ }
+ if (x11->wm_type == 0 && !(vo_fsmode & 16))
+ {
+ XUnmapWindow(x11->display, x11->window); // required for MWM
+ XWithdrawWindow(x11->display, x11->window, x11->screen);
+ x11->fs_flip = 1;
+ }
+
+ if ( ! (x11->fs_type & vo_wm_FULLSCREEN) ) // not needed with EWMH fs
+ {
+ vo_x11_decoration(vo, vo_border && !vo_fs);
+ vo_x11_sizehint(vo, x, y, w, h, 0);
+ vo_x11_setlayer(vo, x11->window, vo_fs);
+
+
+ XMoveResizeWindow(x11->display, x11->window, x, y, w, h);
+ }
+ /* some WMs lose ontop after fullscreen */
+ if ((!(vo_fs)) & opts->vo_ontop)
+ vo_x11_setlayer(vo, x11->window, opts->vo_ontop);
+
+ XMapRaised(x11->display, x11->window);
+ if ( ! (x11->fs_type & vo_wm_FULLSCREEN) ) // some WMs change window pos on map
+ XMoveResizeWindow(x11->display, x11->window, x, y, w, h);
+ XRaiseWindow(x11->display, x11->window);
+ XFlush(x11->display);
+}
+
+void vo_x11_ontop(struct vo *vo)
+{
+ struct MPOpts *opts = vo->opts;
+ opts->vo_ontop = !opts->vo_ontop;
+
+ vo_x11_setlayer(vo, vo->x11->window, opts->vo_ontop);
+}
+
+void vo_x11_border(struct vo *vo)
+{
+ vo_border = !vo_border;
+ vo_x11_decoration(vo, vo_border && !vo_fs);
+}
+
+/*
+ * XScreensaver stuff
+ */
+
+static int screensaver_off;
+static unsigned int time_last;
+
+void xscreensaver_heartbeat(struct vo_x11_state *x11)
+{
+ unsigned int time = GetTimerMS();
+
+ if (x11->display && screensaver_off && (time - time_last) > 30000)
+ {
+ time_last = time;
+
+ XResetScreenSaver(x11->display);
+ }
+}
+
+static int xss_suspend(Display *mDisplay, Bool suspend)
+{
+#ifndef CONFIG_XSS
+ return 0;
+#else
+ int event, error, major, minor;
+ if (XScreenSaverQueryExtension(mDisplay, &event, &error) != True ||
+ XScreenSaverQueryVersion(mDisplay, &major, &minor) != True)
+ return 0;
+ if (major < 1 || (major == 1 && minor < 1))
+ return 0;
+ XScreenSaverSuspend(mDisplay, suspend);
+ return 1;
+#endif
+}
+
+/*
+ * End of XScreensaver stuff
+ */
+
+static void saver_on(Display * mDisplay)
+{
+
+ if (!screensaver_off)
+ return;
+ screensaver_off = 0;
+ if (xss_suspend(mDisplay, False))
+ return;
+#ifdef CONFIG_XDPMS
+ if (dpms_disabled)
+ {
+ int nothing;
+ if (DPMSQueryExtension(mDisplay, &nothing, &nothing))
+ {
+ if (!DPMSEnable(mDisplay))
+ { // restoring power saving settings
+ mp_msg(MSGT_VO, MSGL_WARN, "DPMS not available?\n");
+ } else
+ {
+ // DPMS does not seem to be enabled unless we call DPMSInfo
+ BOOL onoff;
+ CARD16 state;
+
+ DPMSForceLevel(mDisplay, DPMSModeOn);
+ DPMSInfo(mDisplay, &state, &onoff);
+ if (onoff)
+ {
+ mp_msg(MSGT_VO, MSGL_V,
+ "Successfully enabled DPMS\n");
+ } else
+ {
+ mp_msg(MSGT_VO, MSGL_WARN, "Could not enable DPMS\n");
+ }
+ }
+ }
+ dpms_disabled = 0;
+ }
+#endif
+}
+
+static void saver_off(Display * mDisplay)
+{
+ int nothing;
+
+ if (!stop_xscreensaver || screensaver_off)
+ return;
+ screensaver_off = 1;
+ if (xss_suspend(mDisplay, True))
+ return;
+#ifdef CONFIG_XDPMS
+ if (DPMSQueryExtension(mDisplay, &nothing, &nothing))
+ {
+ BOOL onoff;
+ CARD16 state;
+
+ DPMSInfo(mDisplay, &state, &onoff);
+ if (onoff)
+ {
+ Status stat;
+
+ mp_msg(MSGT_VO, MSGL_V, "Disabling DPMS\n");
+ dpms_disabled = 1;
+ stat = DPMSDisable(mDisplay); // monitor powersave off
+ mp_msg(MSGT_VO, MSGL_V, "DPMSDisable stat: %d\n", stat);
+ }
+ }
+#endif
+}
+
+static XErrorHandler old_handler = NULL;
+static int selectinput_err = 0;
+static int x11_selectinput_errorhandler(Display * display,
+ XErrorEvent * event)
+{
+ if (event->error_code == BadAccess)
+ {
+ selectinput_err = 1;
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "X11 error: BadAccess during XSelectInput Call\n");
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "X11 error: The 'ButtonPressMask' mask of specified window has probably already used by another appication (see man XSelectInput)\n");
+ /* If you think MPlayer should shutdown with this error,
+ * comment out the following line */
+ return 0;
+ }
+ if (old_handler != NULL)
+ old_handler(display, event);
+ else
+ x11_errorhandler(display, event);
+ return 0;
+}
+
+void vo_x11_selectinput_witherr(Display * display, Window w,
+ long event_mask)
+{
+ XSync(display, False);
+ old_handler = XSetErrorHandler(x11_selectinput_errorhandler);
+ selectinput_err = 0;
+ if (vo_nomouse_input)
+ {
+ XSelectInput(display, w,
+ event_mask &
+ (~(ButtonPressMask | ButtonReleaseMask)));
+ } else
+ {
+ XSelectInput(display, w, event_mask);
+ }
+ XSync(display, False);
+ XSetErrorHandler(old_handler);
+ if (selectinput_err)
+ {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "X11 error: mpv discards mouse control (reconfiguring)\n");
+ XSelectInput(display, w,
+ event_mask &
+ (~
+ (ButtonPressMask | ButtonReleaseMask |
+ PointerMotionMask)));
+ }
+}
+
+#ifdef CONFIG_XF86VM
+void vo_vm_switch(struct vo *vo)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ struct MPOpts *opts = vo->opts;
+ Display *mDisplay = x11->display;
+ int vm_event, vm_error;
+ int vm_ver, vm_rev;
+ int i, j, have_vm = 0;
+ int X = vo->dwidth, Y = vo->dheight;
+ int modeline_width, modeline_height;
+
+ if (XF86VidModeQueryExtension(mDisplay, &vm_event, &vm_error))
+ {
+ XF86VidModeQueryVersion(mDisplay, &vm_ver, &vm_rev);
+ mp_msg(MSGT_VO, MSGL_V, "XF86VidMode extension v%i.%i\n", vm_ver,
+ vm_rev);
+ have_vm = 1;
+ } else {
+ mp_msg(MSGT_VO, MSGL_WARN,
+ "XF86VidMode extension not available.\n");
+ }
+
+ if (have_vm)
+ {
+ if (vidmodes == NULL)
+ XF86VidModeGetAllModeLines(mDisplay, x11->screen, &modecount,
+ &vidmodes);
+ j = 0;
+ modeline_width = vidmodes[0]->hdisplay;
+ modeline_height = vidmodes[0]->vdisplay;
+
+ for (i = 1; i < modecount; i++)
+ if ((vidmodes[i]->hdisplay >= X)
+ && (vidmodes[i]->vdisplay >= Y))
+ if ((vidmodes[i]->hdisplay <= modeline_width)
+ && (vidmodes[i]->vdisplay <= modeline_height))
+ {
+ modeline_width = vidmodes[i]->hdisplay;
+ modeline_height = vidmodes[i]->vdisplay;
+ j = i;
+ }
+
+ mp_tmsg(MSGT_VO, MSGL_INFO, "XF86VM: Selected video mode %dx%d for image size %dx%d.\n",
+ modeline_width, modeline_height, X, Y);
+ XF86VidModeLockModeSwitch(mDisplay, x11->screen, 0);
+ XF86VidModeSwitchToMode(mDisplay, x11->screen, vidmodes[j]);
+ XF86VidModeSwitchToMode(mDisplay, x11->screen, vidmodes[j]);
+
+ // FIXME: all this is more of a hack than proper solution
+ X = (opts->vo_screenwidth - modeline_width) / 2;
+ Y = (opts->vo_screenheight - modeline_height) / 2;
+ XF86VidModeSetViewPort(mDisplay, x11->screen, X, Y);
+ vo->dx = X;
+ vo->dy = Y;
+ vo->dwidth = modeline_width;
+ vo->dheight = modeline_height;
+ aspect_save_screenres(vo, modeline_width, modeline_height);
+ }
+}
+
+void vo_vm_close(struct vo *vo)
+{
+ Display *dpy = vo->x11->display;
+ struct MPOpts *opts = vo->opts;
+ if (vidmodes != NULL)
+ {
+ int i;
+
+ free(vidmodes);
+ vidmodes = NULL;
+ XF86VidModeGetAllModeLines(dpy, vo->x11->screen, &modecount,
+ &vidmodes);
+ for (i = 0; i < modecount; i++)
+ if ((vidmodes[i]->hdisplay == opts->vo_screenwidth)
+ && (vidmodes[i]->vdisplay == opts->vo_screenheight))
+ {
+ mp_msg(MSGT_VO, MSGL_INFO,
+ "Returning to original mode %dx%d\n",
+ opts->vo_screenwidth, opts->vo_screenheight);
+ break;
+ }
+
+ XF86VidModeSwitchToMode(dpy, vo->x11->screen, vidmodes[i]);
+ XF86VidModeSwitchToMode(dpy, vo->x11->screen, vidmodes[i]);
+ free(vidmodes);
+ vidmodes = NULL;
+ modecount = 0;
+ }
+}
+
+double vo_vm_get_fps(struct vo *vo)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ int clock;
+ XF86VidModeModeLine modeline;
+ if (!XF86VidModeGetModeLine(x11->display, x11->screen, &clock, &modeline))
+ return 0;
+ if (modeline.privsize)
+ XFree(modeline.private);
+ return 1e3 * clock / modeline.htotal / modeline.vtotal;
+}
+#endif
+
+
+/*
+ * Scan the available visuals on this Display/Screen. Try to find
+ * the 'best' available TrueColor visual that has a decent color
+ * depth (at least 15bit). If there are multiple visuals with depth
+ * >= 15bit, we prefer visuals with a smaller color depth.
+ */
+int vo_find_depth_from_visuals(Display * dpy, int screen,
+ Visual ** visual_return)
+{
+ XVisualInfo visual_tmpl;
+ XVisualInfo *visuals;
+ int nvisuals, i;
+ int bestvisual = -1;
+ int bestvisual_depth = -1;
+
+ visual_tmpl.screen = screen;
+ visual_tmpl.class = TrueColor;
+ visuals = XGetVisualInfo(dpy,
+ VisualScreenMask | VisualClassMask,
+ &visual_tmpl, &nvisuals);
+ if (visuals != NULL)
+ {
+ for (i = 0; i < nvisuals; i++)
+ {
+ mp_msg(MSGT_VO, MSGL_V,
+ "vo: X11 truecolor visual %#lx, depth %d, R:%lX G:%lX B:%lX\n",
+ visuals[i].visualid, visuals[i].depth,
+ visuals[i].red_mask, visuals[i].green_mask,
+ visuals[i].blue_mask);
+ /*
+ * Save the visual index and its depth, if this is the first
+ * truecolor visul, or a visual that is 'preferred' over the
+ * previous 'best' visual.
+ */
+ if (bestvisual_depth == -1
+ || (visuals[i].depth >= 15
+ && (visuals[i].depth < bestvisual_depth
+ || bestvisual_depth < 15)))
+ {
+ bestvisual = i;
+ bestvisual_depth = visuals[i].depth;
+ }
+ }
+
+ if (bestvisual != -1 && visual_return != NULL)
+ *visual_return = visuals[bestvisual].visual;
+
+ XFree(visuals);
+ }
+ return bestvisual_depth;
+}
+
+
+static Colormap cmap = None;
+static XColor cols[256];
+static int cm_size, red_mask, green_mask, blue_mask;
+
+
+Colormap vo_x11_create_colormap(struct vo *vo, XVisualInfo *vinfo)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ unsigned k, r, g, b, ru, gu, bu, m, rv, gv, bv, rvu, gvu, bvu;
+
+ if (vinfo->class != DirectColor)
+ return XCreateColormap(x11->display, x11->rootwin, vinfo->visual,
+ AllocNone);
+
+ /* can this function get called twice or more? */
+ if (cmap)
+ return cmap;
+ cm_size = vinfo->colormap_size;
+ red_mask = vinfo->red_mask;
+ green_mask = vinfo->green_mask;
+ blue_mask = vinfo->blue_mask;
+ ru = (red_mask & (red_mask - 1)) ^ red_mask;
+ gu = (green_mask & (green_mask - 1)) ^ green_mask;
+ bu = (blue_mask & (blue_mask - 1)) ^ blue_mask;
+ rvu = 65536ull * ru / (red_mask + ru);
+ gvu = 65536ull * gu / (green_mask + gu);
+ bvu = 65536ull * bu / (blue_mask + bu);
+ r = g = b = 0;
+ rv = gv = bv = 0;
+ m = DoRed | DoGreen | DoBlue;
+ for (k = 0; k < cm_size; k++)
+ {
+ int t;
+
+ cols[k].pixel = r | g | b;
+ cols[k].red = rv;
+ cols[k].green = gv;
+ cols[k].blue = bv;
+ cols[k].flags = m;
+ t = (r + ru) & red_mask;
+ if (t < r)
+ m &= ~DoRed;
+ r = t;
+ t = (g + gu) & green_mask;
+ if (t < g)
+ m &= ~DoGreen;
+ g = t;
+ t = (b + bu) & blue_mask;
+ if (t < b)
+ m &= ~DoBlue;
+ b = t;
+ rv += rvu;
+ gv += gvu;
+ bv += bvu;
+ }
+ cmap = XCreateColormap(x11->display, x11->rootwin, vinfo->visual, AllocAll);
+ XStoreColors(x11->display, cmap, cols, cm_size);
+ return cmap;
+}
+
+/*
+ * Via colormaps/gamma ramps we can do gamma, brightness, contrast,
+ * hue and red/green/blue intensity, but we cannot do saturation.
+ * Currently only gamma, brightness and contrast are implemented.
+ * Is there sufficient interest for hue and/or red/green/blue intensity?
+ */
+/* these values have range [-100,100] and are initially 0 */
+static int vo_gamma = 0;
+static int vo_brightness = 0;
+static int vo_contrast = 0;
+
+static int transform_color(float val,
+ float brightness, float contrast, float gamma) {
+ float s = pow(val, gamma);
+ s = (s - 0.5) * contrast + 0.5;
+ s += brightness;
+ if (s < 0)
+ s = 0;
+ if (s > 1)
+ s = 1;
+ return (unsigned short) (s * 65535);
+}
+
+uint32_t vo_x11_set_equalizer(struct vo *vo, const char *name, int value)
+{
+ float gamma, brightness, contrast;
+ float rf, gf, bf;
+ int k;
+
+ /*
+ * IMPLEMENTME: consider using XF86VidModeSetGammaRamp in the case
+ * of TrueColor-ed window but be careful:
+ * Unlike the colormaps, which are private for the X client
+ * who created them and thus automatically destroyed on client
+ * disconnect, this gamma ramp is a system-wide (X-server-wide)
+ * setting and _must_ be restored before the process exits.
+ * Unforunately when the process crashes (or gets killed
+ * for some reason) it is impossible to restore the setting,
+ * and such behaviour could be rather annoying for the users.
+ */
+ if (cmap == None)
+ return VO_NOTAVAIL;
+
+ if (!strcasecmp(name, "brightness"))
+ vo_brightness = value;
+ else if (!strcasecmp(name, "contrast"))
+ vo_contrast = value;
+ else if (!strcasecmp(name, "gamma"))
+ vo_gamma = value;
+ else
+ return VO_NOTIMPL;
+
+ brightness = 0.01 * vo_brightness;
+ contrast = tan(0.0095 * (vo_contrast + 100) * M_PI / 4);
+ gamma = pow(2, -0.02 * vo_gamma);
+
+ rf = (float) ((red_mask & (red_mask - 1)) ^ red_mask) / red_mask;
+ gf = (float) ((green_mask & (green_mask - 1)) ^ green_mask) /
+ green_mask;
+ bf = (float) ((blue_mask & (blue_mask - 1)) ^ blue_mask) / blue_mask;
+
+ /* now recalculate the colormap using the newly set value */
+ for (k = 0; k < cm_size; k++)
+ {
+ cols[k].red = transform_color(rf * k, brightness, contrast, gamma);
+ cols[k].green = transform_color(gf * k, brightness, contrast, gamma);
+ cols[k].blue = transform_color(bf * k, brightness, contrast, gamma);
+ }
+
+ XStoreColors(vo->x11->display, cmap, cols, cm_size);
+ XFlush(vo->x11->display);
+ return VO_TRUE;
+}
+
+uint32_t vo_x11_get_equalizer(const char *name, int *value)
+{
+ if (cmap == None)
+ return VO_NOTAVAIL;
+ if (!strcasecmp(name, "brightness"))
+ *value = vo_brightness;
+ else if (!strcasecmp(name, "contrast"))
+ *value = vo_contrast;
+ else if (!strcasecmp(name, "gamma"))
+ *value = vo_gamma;
+ else
+ return VO_NOTIMPL;
+ return VO_TRUE;
+}
+
+bool vo_x11_screen_is_composited(struct vo *vo)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ return XGetSelectionOwner(x11->display, x11->XA_NET_WM_CM) != None;
+}
+
+#ifdef CONFIG_XV
+
+static int xv_find_atom(struct vo *vo, uint32_t xv_port, const char *name,
+ bool get, int *min, int *max)
+{
+ Atom atom = None;
+ int howmany = 0;
+ XvAttribute *attributes = XvQueryPortAttributes(vo->x11->display, xv_port,
+ &howmany);
+ for (int i = 0; i < howmany && attributes; i++) {
+ int flag = get ? XvGettable : XvSettable;
+ if (attributes[i].flags & flag) {
+ atom = XInternAtom(vo->x11->display, attributes[i].name, True);
+ *min = attributes[i].min_value;
+ *max = attributes[i].max_value;
+/* since we have SET_DEFAULTS first in our list, we can check if it's available
+ then trigger it if it's ok so that the other values are at default upon query */
+ if (atom != None) {
+ if (!strcmp(attributes[i].name, "XV_BRIGHTNESS") &&
+ (!strcasecmp(name, "brightness")))
+ break;
+ else if (!strcmp(attributes[i].name, "XV_CONTRAST") &&
+ (!strcasecmp(name, "contrast")))
+ break;
+ else if (!strcmp(attributes[i].name, "XV_SATURATION") &&
+ (!strcasecmp(name, "saturation")))
+ break;
+ else if (!strcmp(attributes[i].name, "XV_HUE") &&
+ (!strcasecmp(name, "hue")))
+ break;
+ if (!strcmp(attributes[i].name, "XV_RED_INTENSITY") &&
+ (!strcasecmp(name, "red_intensity")))
+ break;
+ else if (!strcmp(attributes[i].name, "XV_GREEN_INTENSITY")
+ && (!strcasecmp(name, "green_intensity")))
+ break;
+ else if (!strcmp(attributes[i].name, "XV_BLUE_INTENSITY")
+ && (!strcasecmp(name, "blue_intensity")))
+ break;
+ else if ((!strcmp(attributes[i].name, "XV_ITURBT_709") //NVIDIA
+ || !strcmp(attributes[i].name, "XV_COLORSPACE"))//ATI
+ && (!strcasecmp(name, "bt_709")))
+ break;
+ atom = None;
+ continue;
+ }
+ }
+ }
+ XFree(attributes);
+ return atom;
+}
+
+int vo_xv_set_eq(struct vo *vo, uint32_t xv_port, const char *name, int value)
+{
+ mp_dbg(MSGT_VO, MSGL_V, "xv_set_eq called! (%s, %d)\n", name, value);
+
+ int min, max;
+ int atom = xv_find_atom(vo, xv_port, name, false, &min, &max);
+ if (atom != None) {
+ // -100 -> min
+ // 0 -> (max+min)/2
+ // +100 -> max
+ int port_value = (value + 100) * (max - min) / 200 + min;
+ XvSetPortAttribute(vo->x11->display, xv_port, atom, port_value);
+ return VO_TRUE;
+ }
+ return VO_FALSE;
+}
+
+int vo_xv_get_eq(struct vo *vo, uint32_t xv_port, const char *name, int *value)
+{
+ int min, max;
+ int atom = xv_find_atom(vo, xv_port, name, true, &min, &max);
+ if (atom != None) {
+ int port_value = 0;
+ XvGetPortAttribute(vo->x11->display, xv_port, atom, &port_value);
+
+ *value = (port_value - min) * 200 / (max - min) - 100;
+ mp_dbg(MSGT_VO, MSGL_V, "xv_get_eq called! (%s, %d)\n",
+ name, *value);
+ return VO_TRUE;
+ }
+ return VO_FALSE;
+}
+
+/**
+ * \brief Interns the requested atom if it is available.
+ *
+ * \param atom_name String containing the name of the requested atom.
+ *
+ * \return Returns the atom if available, else None is returned.
+ *
+ */
+static Atom xv_intern_atom_if_exists(struct vo_x11_state *x11,
+ char const *atom_name)
+{
+ XvAttribute * attributes;
+ int attrib_count,i;
+ Atom xv_atom = None;
+
+ attributes = XvQueryPortAttributes(x11->display, x11->xv_port, &attrib_count );
+ if( attributes!=NULL )
+ {
+ for ( i = 0; i < attrib_count; ++i )
+ {
+ if ( strcmp(attributes[i].name, atom_name ) == 0 )
+ {
+ xv_atom = XInternAtom(x11->display, atom_name, False );
+ break; // found what we want, break out
+ }
+ }
+ XFree( attributes );
+ }
+
+ return xv_atom;
+}
+
+/**
+ * \brief Try to enable vsync for xv.
+ * \return Returns -1 if not available, 0 on failure and 1 on success.
+ */
+int vo_xv_enable_vsync(struct vo *vo)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ Atom xv_atom = xv_intern_atom_if_exists(x11, "XV_SYNC_TO_VBLANK");
+ if (xv_atom == None)
+ return -1;
+ return XvSetPortAttribute(x11->display, x11->xv_port, xv_atom, 1) == Success;
+}
+
+/**
+ * \brief Get maximum supported source image dimensions.
+ *
+ * This function does not set the variables pointed to by
+ * width and height if the information could not be retrieved,
+ * so the caller is reponsible for properly initializing them.
+ *
+ * \param width [out] The maximum width gets stored here.
+ * \param height [out] The maximum height gets stored here.
+ *
+ */
+void vo_xv_get_max_img_dim(struct vo *vo, uint32_t * width, uint32_t * height)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ XvEncodingInfo * encodings;
+ //unsigned long num_encodings, idx; to int or too long?!
+ unsigned int num_encodings, idx;
+
+ XvQueryEncodings(x11->display, x11->xv_port, &num_encodings, &encodings);
+
+ if ( encodings )
+ {
+ for ( idx = 0; idx < num_encodings; ++idx )
+ {
+ if ( strcmp( encodings[idx].name, "XV_IMAGE" ) == 0 )
+ {
+ *width = encodings[idx].width;
+ *height = encodings[idx].height;
+ break;
+ }
+ }
+ }
+
+ mp_msg( MSGT_VO, MSGL_V,
+ "[xv common] Maximum source image dimensions: %ux%u\n",
+ *width, *height );
+
+ XvFreeEncodingInfo( encodings );
+}
+
+/**
+ * \brief Print information about the colorkey method and source.
+ *
+ * \param ck_handling Integer value containing the information about
+ * colorkey handling (see x11_common.h).
+ *
+ * Outputs the content of |ck_handling| as a readable message.
+ *
+ */
+static void vo_xv_print_ck_info(struct vo_x11_state *x11)
+{
+ mp_msg( MSGT_VO, MSGL_V, "[xv common] " );
+
+ switch ( x11->xv_ck_info.method )
+ {
+ case CK_METHOD_NONE:
+ mp_msg( MSGT_VO, MSGL_V, "Drawing no colorkey.\n" ); return;
+ case CK_METHOD_AUTOPAINT:
+ mp_msg( MSGT_VO, MSGL_V, "Colorkey is drawn by Xv." ); break;
+ case CK_METHOD_MANUALFILL:
+ mp_msg( MSGT_VO, MSGL_V, "Drawing colorkey manually." ); break;
+ case CK_METHOD_BACKGROUND:
+ mp_msg( MSGT_VO, MSGL_V, "Colorkey is drawn as window background." ); break;
+ }
+
+ mp_msg( MSGT_VO, MSGL_V, "\n[xv common] " );
+
+ switch ( x11->xv_ck_info.source )
+ {
+ case CK_SRC_CUR:
+ mp_msg( MSGT_VO, MSGL_V, "Using colorkey from Xv (0x%06lx).\n",
+ x11->xv_colorkey );
+ break;
+ case CK_SRC_USE:
+ if ( x11->xv_ck_info.method == CK_METHOD_AUTOPAINT )
+ {
+ mp_msg( MSGT_VO, MSGL_V,
+ "Ignoring colorkey from mpv (0x%06lx).\n",
+ x11->xv_colorkey );
+ }
+ else
+ {
+ mp_msg( MSGT_VO, MSGL_V,
+ "Using colorkey from mpv (0x%06lx)."
+ " Use -colorkey to change.\n",
+ x11->xv_colorkey );
+ }
+ break;
+ case CK_SRC_SET:
+ mp_msg( MSGT_VO, MSGL_V,
+ "Setting and using colorkey from mpv (0x%06lx)."
+ " Use -colorkey to change.\n",
+ x11->xv_colorkey );
+ break;
+ }
+}
+/**
+ * \brief Init colorkey depending on the settings in xv_ck_info.
+ *
+ * \return Returns 0 on failure and 1 on success.
+ *
+ * Sets the colorkey variable according to the CK_SRC_* and CK_METHOD_*
+ * flags in xv_ck_info.
+ *
+ * Possiblilities:
+ * * Methods
+ * - manual colorkey drawing ( CK_METHOD_MANUALFILL )
+ * - set colorkey as window background ( CK_METHOD_BACKGROUND )
+ * - let Xv paint the colorkey ( CK_METHOD_AUTOPAINT )
+ * * Sources
+ * - use currently set colorkey ( CK_SRC_CUR )
+ * - use colorkey in vo_colorkey ( CK_SRC_USE )
+ * - use and set colorkey in vo_colorkey ( CK_SRC_SET )
+ *
+ * NOTE: If vo_colorkey has bits set after the first 3 low order bytes
+ * we don't draw anything as this means it was forced to off.
+ */
+int vo_xv_init_colorkey(struct vo *vo)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ Atom xv_atom;
+ int rez;
+
+ /* check if colorkeying is needed */
+ xv_atom = xv_intern_atom_if_exists(vo->x11, "XV_COLORKEY");
+
+ /* if we have to deal with colorkeying ... */
+ if( xv_atom != None && !(vo_colorkey & 0xFF000000) )
+ {
+ /* check if we should use the colorkey specified in vo_colorkey */
+ if ( x11->xv_ck_info.source != CK_SRC_CUR )
+ {
+ x11->xv_colorkey = vo_colorkey;
+
+ /* check if we have to set the colorkey too */
+ if ( x11->xv_ck_info.source == CK_SRC_SET )
+ {
+ xv_atom = XInternAtom(x11->display, "XV_COLORKEY",False);
+
+ rez = XvSetPortAttribute(x11->display, x11->xv_port, xv_atom, vo_colorkey);
+ if ( rez != Success )
+ {
+ mp_msg( MSGT_VO, MSGL_FATAL,
+ "[xv common] Couldn't set colorkey!\n" );
+ return 0; // error setting colorkey
+ }
+ }
+ }
+ else
+ {
+ int colorkey_ret;
+
+ rez=XvGetPortAttribute(x11->display,x11->xv_port, xv_atom, &colorkey_ret);
+ if ( rez == Success )
+ {
+ x11->xv_colorkey = colorkey_ret;
+ }
+ else
+ {
+ mp_msg( MSGT_VO, MSGL_FATAL,
+ "[xv common] Couldn't get colorkey!"
+ "Maybe the selected Xv port has no overlay.\n" );
+ return 0; // error getting colorkey
+ }
+ }
+
+ xv_atom = xv_intern_atom_if_exists(vo->x11, "XV_AUTOPAINT_COLORKEY");
+
+ /* should we draw the colorkey ourselves or activate autopainting? */
+ if ( x11->xv_ck_info.method == CK_METHOD_AUTOPAINT )
+ {
+ rez = !Success; // reset rez to something different than Success
+
+ if ( xv_atom != None ) // autopaint is supported
+ {
+ rez = XvSetPortAttribute(x11->display, x11->xv_port, xv_atom, 1);
+ }
+
+ if ( rez != Success )
+ {
+ // fallback to manual colorkey drawing
+ x11->xv_ck_info.method = CK_METHOD_MANUALFILL;
+ }
+ }
+ else // disable colorkey autopainting if supported
+ {
+ if ( xv_atom != None ) // we have autopaint attribute
+ {
+ XvSetPortAttribute(x11->display, x11->xv_port, xv_atom, 0);
+ }
+ }
+ }
+ else // do no colorkey drawing at all
+ {
+ x11->xv_ck_info.method = CK_METHOD_NONE;
+ } /* end: should we draw colorkey */
+
+ /* output information about the current colorkey settings */
+ vo_xv_print_ck_info(x11);
+
+ return 1; // success
+}
+
+/**
+ * \brief Draw the colorkey on the video window.
+ *
+ * Draws the colorkey depending on the set method ( colorkey_handling ).
+ *
+ * Also draws the black bars ( when the video doesn't fit the display in
+ * fullscreen ) separately, so they don't overlap with the video area.
+ * It doesn't call XFlush.
+ *
+ */
+void vo_xv_draw_colorkey(struct vo *vo, int32_t x, int32_t y,
+ int32_t w, int32_t h)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ if( x11->xv_ck_info.method == CK_METHOD_MANUALFILL ||
+ x11->xv_ck_info.method == CK_METHOD_BACKGROUND )//less tearing than XClearWindow()
+ {
+ XSetForeground(x11->display, x11->vo_gc, x11->xv_colorkey );
+ XFillRectangle(x11->display, x11->window, x11->vo_gc,
+ x, y,
+ w, h );
+ }
+}
+
+/** \brief Tests if a valid argument for the ck suboption was given. */
+int xv_test_ck( void * arg )
+{
+ strarg_t * strarg = (strarg_t *)arg;
+
+ if ( strargcmp( strarg, "use" ) == 0 ||
+ strargcmp( strarg, "set" ) == 0 ||
+ strargcmp( strarg, "cur" ) == 0 )
+ {
+ return 1;
+ }
+
+ return 0;
+}
+/** \brief Tests if a valid arguments for the ck-method suboption was given. */
+int xv_test_ckm( void * arg )
+{
+ strarg_t * strarg = (strarg_t *)arg;
+
+ if ( strargcmp( strarg, "bg" ) == 0 ||
+ strargcmp( strarg, "man" ) == 0 ||
+ strargcmp( strarg, "auto" ) == 0 )
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Modify the colorkey_handling var according to str
+ *
+ * Checks if a valid pointer ( not NULL ) to the string
+ * was given. And in that case modifies the colorkey_handling
+ * var to reflect the requested behaviour.
+ * If nothing happens the content of colorkey_handling stays
+ * the same.
+ *
+ * \param str Pointer to the string or NULL
+ *
+ */
+void xv_setup_colorkeyhandling(struct vo *vo, const char *ck_method_str,
+ const char *ck_str)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ /* check if a valid pointer to the string was passed */
+ if ( ck_str )
+ {
+ if ( strncmp( ck_str, "use", 3 ) == 0 )
+ {
+ x11->xv_ck_info.source = CK_SRC_USE;
+ }
+ else if ( strncmp( ck_str, "set", 3 ) == 0 )
+ {
+ x11->xv_ck_info.source = CK_SRC_SET;
+ }
+ }
+ /* check if a valid pointer to the string was passed */
+ if ( ck_method_str )
+ {
+ if ( strncmp( ck_method_str, "bg", 2 ) == 0 )
+ {
+ x11->xv_ck_info.method = CK_METHOD_BACKGROUND;
+ }
+ else if ( strncmp( ck_method_str, "man", 3 ) == 0 )
+ {
+ x11->xv_ck_info.method = CK_METHOD_MANUALFILL;
+ }
+ else if ( strncmp( ck_method_str, "auto", 4 ) == 0 )
+ {
+ x11->xv_ck_info.method = CK_METHOD_AUTOPAINT;
+ }
+ }
+}
+
+#endif
+
+struct vo_x11_state *vo_x11_init_state(void)
+{
+ struct vo_x11_state *s = talloc_ptrtype(NULL, s);
+ *s = (struct vo_x11_state){
+ .xv_ck_info = { CK_METHOD_MANUALFILL, CK_SRC_CUR },
+ .olddecor = MWM_DECOR_ALL,
+ .oldfuncs = MWM_FUNC_MOVE | MWM_FUNC_CLOSE | MWM_FUNC_MINIMIZE |
+ MWM_FUNC_MAXIMIZE | MWM_FUNC_RESIZE,
+ .old_gravity = NorthWestGravity,
+ };
+ return s;
+}
diff --git a/video/out/x11_common.h b/video/out/x11_common.h
new file mode 100644
index 0000000000..cb8b39a3b1
--- /dev/null
+++ b/video/out/x11_common.h
@@ -0,0 +1,184 @@
+/*
+ * 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_X11_COMMON_H
+#define MPLAYER_X11_COMMON_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include "config.h"
+
+struct vo;
+
+struct vo_x11_state {
+ Display *display;
+ Window window;
+ Window rootwin;
+ int screen;
+ int display_is_local;
+ int depthonscreen;
+
+ XIM xim;
+ XIC xic;
+
+ GC vo_gc;
+
+ struct xv_ck_info_s {
+ int method; ///< CK_METHOD_* constants
+ int source; ///< CK_SRC_* constants
+ } xv_ck_info;
+ unsigned long xv_colorkey;
+ unsigned int xv_port;
+
+ int wm_type;
+ int fs_type;
+ int window_state;
+ int fs_flip;
+
+ GC f_gc;
+ XSizeHints vo_hint;
+ unsigned int mouse_timer;
+ int mouse_waiting_hide;
+ int orig_layer;
+ int old_gravity;
+ int vo_old_x;
+ int vo_old_y;
+ int vo_old_width;
+ int vo_old_height;
+
+ /* Keep track of original video width/height to determine when to
+ * resize window when reconfiguring. Resize window when video size
+ * changes, but don't force window size changes as long as video size
+ * stays the same (even if that size is different from the current
+ * window size after the user modified the latter). */
+ int last_video_width;
+ int last_video_height;
+ /* Video size changed during fullscreen when we couldn't tell the new
+ * size to the window manager. Must set window size when turning
+ * fullscreen off. */
+ bool size_changed_during_fs;
+
+ unsigned int olddecor;
+ unsigned int oldfuncs;
+ XComposeStatus compose_status;
+
+ Atom XA_NET_SUPPORTED;
+ Atom XA_NET_WM_STATE;
+ Atom XA_NET_WM_STATE_FULLSCREEN;
+ Atom XA_NET_WM_STATE_ABOVE;
+ Atom XA_NET_WM_STATE_STAYS_ON_TOP;
+ Atom XA_NET_WM_STATE_BELOW;
+ Atom XA_NET_WM_PID;
+ Atom XA_NET_WM_NAME;
+ Atom XA_NET_WM_ICON_NAME;
+ Atom XA_WIN_PROTOCOLS;
+ Atom XA_WIN_LAYER;
+ Atom XA_WIN_HINTS;
+ Atom XAWM_PROTOCOLS;
+ Atom XAWM_DELETE_WINDOW;
+ Atom XAUTF8_STRING;
+ Atom XA_NET_WM_CM;
+};
+
+#define vo_wm_LAYER 1
+#define vo_wm_FULLSCREEN 2
+#define vo_wm_STAYS_ON_TOP 4
+#define vo_wm_ABOVE 8
+#define vo_wm_BELOW 16
+#define vo_wm_NETWM (vo_wm_FULLSCREEN | vo_wm_STAYS_ON_TOP | vo_wm_ABOVE | vo_wm_BELOW)
+
+/* EWMH state actions, see
+ http://freedesktop.org/Standards/wm-spec/index.html#id2768769 */
+#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */
+#define _NET_WM_STATE_ADD 1 /* add/set property */
+#define _NET_WM_STATE_TOGGLE 2 /* toggle property */
+
+extern int metacity_hack;
+
+extern char** vo_fstype_list;
+
+extern char *mDisplayName;
+
+struct vo_x11_state *vo_x11_init_state(void);
+int vo_init(struct vo *vo);
+void vo_uninit(struct vo_x11_state *x11);
+void vo_x11_decoration(struct vo *vo, int d );
+void vo_x11_classhint(struct vo *vo, Window window, const char *name);
+void vo_x11_sizehint(struct vo *vo, int x, int y, int width, int height, int max);
+int vo_x11_check_events(struct vo *vo);
+void vo_x11_selectinput_witherr(Display *display, Window w, long event_mask);
+void vo_x11_fullscreen(struct vo *vo);
+int vo_x11_update_geometry(struct vo *vo, bool update_pos);
+void vo_x11_setlayer(struct vo *vo, Window vo_window, int layer);
+void vo_x11_uninit(struct vo *vo);
+Colormap vo_x11_create_colormap(struct vo *vo, XVisualInfo *vinfo);
+uint32_t vo_x11_set_equalizer(struct vo *vo, const char *name, int value);
+uint32_t vo_x11_get_equalizer(const char *name, int *value);
+bool vo_x11_screen_is_composited(struct vo *vo);
+void fstype_help(void);
+void vo_x11_create_vo_window(struct vo *vo, XVisualInfo *vis,
+ int x, int y, unsigned int width, unsigned int height, int flags,
+ Colormap col_map, const char *classname);
+void vo_x11_clearwindow_part(struct vo *vo, Window vo_window,
+ int img_width, int img_height);
+void vo_x11_clearwindow(struct vo *vo, Window vo_window);
+void vo_x11_ontop(struct vo *vo);
+void vo_x11_border(struct vo *vo);
+void vo_x11_ewmh_fullscreen(struct vo_x11_state *x11, int action);
+
+
+int vo_xv_set_eq(struct vo *vo, uint32_t xv_port, const char *name, int value);
+int vo_xv_get_eq(struct vo *vo, uint32_t xv_port, const char *name, int *value);
+
+int vo_xv_enable_vsync(struct vo *vo);
+
+void vo_xv_get_max_img_dim(struct vo *vo, uint32_t * width, uint32_t * height);
+
+/*** colorkey handling ***/
+
+#define CK_METHOD_NONE 0 ///< no colorkey drawing
+#define CK_METHOD_BACKGROUND 1 ///< set colorkey as window background
+#define CK_METHOD_AUTOPAINT 2 ///< let xv draw the colorkey
+#define CK_METHOD_MANUALFILL 3 ///< manually draw the colorkey
+#define CK_SRC_USE 0 ///< use specified / default colorkey
+#define CK_SRC_SET 1 ///< use and set specified / default colorkey
+#define CK_SRC_CUR 2 ///< use current colorkey ( get it from xv )
+
+int vo_xv_init_colorkey(struct vo *vo);
+void vo_xv_draw_colorkey(struct vo *vo, int32_t x, int32_t y, int32_t w, int32_t h);
+void xv_setup_colorkeyhandling(struct vo *vo, const char *ck_method_str, const char *ck_str);
+
+/*** test functions for common suboptions ***/
+int xv_test_ck( void * arg );
+int xv_test_ckm( void * arg );
+
+#ifdef CONFIG_XF86VM
+void vo_vm_switch(struct vo *vo);
+void vo_vm_close(struct vo *vo);
+double vo_vm_get_fps(struct vo *vo);
+#endif
+
+void update_xinerama_info(struct vo *vo);
+
+int vo_find_depth_from_visuals(Display *dpy, int screen, Visual **visual_return);
+void xscreensaver_heartbeat(struct vo_x11_state *x11);
+
+#endif /* MPLAYER_X11_COMMON_H */
diff --git a/video/sws_utils.c b/video/sws_utils.c
new file mode 100644
index 0000000000..4fc5639e55
--- /dev/null
+++ b/video/sws_utils.c
@@ -0,0 +1,199 @@
+/*
+ * 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 <assert.h>
+
+#include <libavutil/opt.h>
+
+#include "libmpcodecs/sws_utils.h"
+
+#include "libmpcodecs/mp_image.h"
+#include "libmpcodecs/img_format.h"
+#include "fmt-conversion.h"
+#include "libvo/csputils.h"
+#include "mp_msg.h"
+
+//global sws_flags from the command line
+int sws_flags = 2;
+
+float sws_lum_gblur = 0.0;
+float sws_chr_gblur = 0.0;
+int sws_chr_vshift = 0;
+int sws_chr_hshift = 0;
+float sws_chr_sharpen = 0.0;
+float sws_lum_sharpen = 0.0;
+
+//global srcFilter
+static SwsFilter *src_filter = NULL;
+
+void sws_getFlagsAndFilterFromCmdLine(int *flags, SwsFilter **srcFilterParam,
+ SwsFilter **dstFilterParam)
+{
+ static int firstTime = 1;
+ *flags = 0;
+
+ if (firstTime) {
+ firstTime = 0;
+ *flags = SWS_PRINT_INFO;
+ } else if (mp_msg_test(MSGT_VFILTER, MSGL_DBG2))
+ *flags = SWS_PRINT_INFO;
+
+ if (src_filter)
+ sws_freeFilter(src_filter);
+
+ src_filter = sws_getDefaultFilter(
+ sws_lum_gblur, sws_chr_gblur,
+ sws_lum_sharpen, sws_chr_sharpen,
+ sws_chr_hshift, sws_chr_vshift, verbose > 1);
+
+ switch (sws_flags) {
+ case 0: *flags |= SWS_FAST_BILINEAR;
+ break;
+ case 1: *flags |= SWS_BILINEAR;
+ break;
+ case 2: *flags |= SWS_BICUBIC;
+ break;
+ case 3: *flags |= SWS_X;
+ break;
+ case 4: *flags |= SWS_POINT;
+ break;
+ case 5: *flags |= SWS_AREA;
+ break;
+ case 6: *flags |= SWS_BICUBLIN;
+ break;
+ case 7: *flags |= SWS_GAUSS;
+ break;
+ case 8: *flags |= SWS_SINC;
+ break;
+ case 9: *flags |= SWS_LANCZOS;
+ break;
+ case 10: *flags |= SWS_SPLINE;
+ break;
+ default: *flags |= SWS_BILINEAR;
+ break;
+ }
+
+ *srcFilterParam = src_filter;
+ *dstFilterParam = NULL;
+}
+
+// will use sws_flags & src_filter (from cmd line)
+static struct SwsContext *sws_getContextFromCmdLine2(int srcW, int srcH,
+ int srcFormat, int dstW,
+ int dstH, int dstFormat,
+ int extraflags)
+{
+ int flags;
+ SwsFilter *dstFilterParam, *srcFilterParam;
+ enum PixelFormat dfmt, sfmt;
+
+ dfmt = imgfmt2pixfmt(dstFormat);
+ sfmt = imgfmt2pixfmt(srcFormat);
+ if (srcFormat == IMGFMT_RGB8 || srcFormat == IMGFMT_BGR8)
+ sfmt = PIX_FMT_PAL8;
+ sws_getFlagsAndFilterFromCmdLine(&flags, &srcFilterParam, &dstFilterParam);
+
+ return sws_getContext(srcW, srcH, sfmt, dstW, dstH, dfmt, flags |
+ extraflags, srcFilterParam, dstFilterParam,
+ NULL);
+}
+
+struct SwsContext *sws_getContextFromCmdLine(int srcW, int srcH, int srcFormat,
+ int dstW, int dstH,
+ int dstFormat)
+{
+ return sws_getContextFromCmdLine2(srcW, srcH, srcFormat, dstW, dstH,
+ dstFormat,
+ 0);
+}
+
+struct SwsContext *sws_getContextFromCmdLine_hq(int srcW, int srcH,
+ int srcFormat, int dstW,
+ int dstH,
+ int dstFormat)
+{
+ return sws_getContextFromCmdLine2(
+ srcW, srcH, srcFormat, dstW, dstH, dstFormat,
+ SWS_FULL_CHR_H_INT | SWS_FULL_CHR_H_INP |
+ SWS_ACCURATE_RND | SWS_BITEXACT);
+}
+
+bool mp_sws_supported_format(int imgfmt)
+{
+ enum PixelFormat av_format = imgfmt2pixfmt(imgfmt);
+
+ return av_format != PIX_FMT_NONE && sws_isSupportedInput(av_format)
+ && sws_isSupportedOutput(av_format);
+}
+
+static int mp_csp_to_sws_colorspace(enum mp_csp csp)
+{
+ switch (csp) {
+ case MP_CSP_BT_601: return SWS_CS_ITU601;
+ case MP_CSP_BT_709: return SWS_CS_ITU709;
+ case MP_CSP_SMPTE_240M: return SWS_CS_SMPTE240M;
+ default: return SWS_CS_DEFAULT;
+ }
+}
+
+void mp_image_swscale(struct mp_image *dst, struct mp_image *src,
+ int my_sws_flags)
+{
+ enum PixelFormat s_fmt = imgfmt2pixfmt(src->imgfmt);
+ if (src->imgfmt == IMGFMT_RGB8 || src->imgfmt == IMGFMT_BGR8)
+ s_fmt = PIX_FMT_PAL8;
+ int s_csp = mp_csp_to_sws_colorspace(mp_image_csp(src));
+ int s_range = mp_image_levels(src) == MP_CSP_LEVELS_PC;
+
+ enum PixelFormat d_fmt = imgfmt2pixfmt(dst->imgfmt);
+ int d_csp = mp_csp_to_sws_colorspace(mp_image_csp(dst));
+ int d_range = mp_image_levels(dst) == MP_CSP_LEVELS_PC;
+
+ // Work around libswscale bug #1852 (fixed in ffmpeg commit 8edf9b1fa):
+ // setting range flags for RGB gives random bogus results.
+ // Newer libswscale always ignores range flags for RGB.
+ bool s_yuv = src->flags & MP_IMGFLAG_YUV;
+ bool d_yuv = dst->flags & MP_IMGFLAG_YUV;
+ s_range = s_range && s_yuv;
+ d_range = d_range && d_yuv;
+
+ struct SwsContext *sws = sws_alloc_context();
+
+ av_opt_set_int(sws, "sws_flags", my_sws_flags, 0);
+
+ av_opt_set_int(sws, "srcw", src->w, 0);
+ av_opt_set_int(sws, "srch", src->h, 0);
+ av_opt_set_int(sws, "src_format", s_fmt, 0);
+
+ av_opt_set_int(sws, "dstw", dst->w, 0);
+ av_opt_set_int(sws, "dsth", dst->h, 0);
+ av_opt_set_int(sws, "dst_format", d_fmt, 0);
+
+ sws_setColorspaceDetails(sws, sws_getCoefficients(s_csp), s_range,
+ sws_getCoefficients(d_csp), d_range,
+ 0, 1 << 16, 1 << 16);
+
+ int res = sws_init_context(sws, NULL, NULL);
+ assert(res >= 0);
+
+ sws_scale(sws, (const uint8_t *const *) src->planes, src->stride,
+ 0, src->h, dst->planes, dst->stride);
+ sws_freeContext(sws);
+}
+
+// vim: ts=4 sw=4 et tw=80
diff --git a/video/sws_utils.h b/video/sws_utils.h
new file mode 100644
index 0000000000..a0cc47d850
--- /dev/null
+++ b/video/sws_utils.h
@@ -0,0 +1,33 @@
+#ifndef MPLAYER_SWS_UTILS_H
+#define MPLAYER_SWS_UTILS_H
+
+#include <stdbool.h>
+#include <libswscale/swscale.h>
+
+struct mp_image;
+struct mp_csp_details;
+
+// libswscale currently requires 16 bytes alignment for row pointers and
+// strides. Otherwise, it will print warnings and use slow codepaths.
+// Guaranteed to be a power of 2 and > 1.
+#define SWS_MIN_BYTE_ALIGN 16
+
+void sws_getFlagsAndFilterFromCmdLine(int *flags, SwsFilter **srcFilterParam,
+ SwsFilter **dstFilterParam);
+struct SwsContext *sws_getContextFromCmdLine(int srcW, int srcH, int srcFormat,
+ int dstW, int dstH,
+ int dstFormat);
+struct SwsContext *sws_getContextFromCmdLine_hq(int srcW, int srcH,
+ int srcFormat, int dstW,
+ int dstH,
+ int dstFormat);
+int mp_sws_set_colorspace(struct SwsContext *sws, struct mp_csp_details *csp);
+
+bool mp_sws_supported_format(int imgfmt);
+
+void mp_image_swscale(struct mp_image *dst, struct mp_image *src,
+ int my_sws_flags);
+
+#endif /* MP_SWS_UTILS_H */
+
+// vim: ts=4 sw=4 et tw=80
diff --git a/video/vfcap.h b/video/vfcap.h
new file mode 100644
index 0000000000..acc7ce31c6
--- /dev/null
+++ b/video/vfcap.h
@@ -0,0 +1,46 @@
+/* VFCAP_* values: they are flags, returned by query_format():
+ *
+ * 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_VFCAP_H
+#define MPLAYER_VFCAP_H
+
+// set, if the given colorspace is supported (with or without conversion)
+#define VFCAP_CSP_SUPPORTED 0x1
+// set, if the given colorspace is supported _without_ conversion
+#define VFCAP_CSP_SUPPORTED_BY_HW 0x2
+// set if the driver/filter can draw OSD
+#define VFCAP_OSD 0x4
+// scaling up/down by hardware, or software:
+#define VFCAP_HWSCALE_UP 0x10
+#define VFCAP_HWSCALE_DOWN 0x20
+#define VFCAP_SWSCALE 0x40
+// driver/filter can do vertical flip (upside-down)
+#define VFCAP_FLIP 0x80
+
+// driver/hardware handles timing (blocking)
+#define VFCAP_TIMER 0x100
+// vf filter: accepts stride (put_image)
+// vo driver: has draw_slice() support for the given csp
+#define VFCAP_ACCEPT_STRIDE 0x400
+// filter does postprocessing (so you shouldn't scale/filter image before it)
+#define VFCAP_POSTPROC 0x800
+// used by libvo and vf_vo, indicates the VO does not support draw_slice for this format
+#define VOCAP_NOSLICES 0x8000
+
+#endif /* MPLAYER_VFCAP_H */