From d4bdd0473d6f43132257c9fb3848d829755167a3 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 5 Nov 2012 17:02:04 +0100 Subject: 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. --- video/csputils.c | 391 ++++++ video/csputils.h | 151 +++ video/decode/dec_video.c | 448 +++++++ video/decode/dec_video.h | 51 + video/decode/vd.c | 273 ++++ video/decode/vd.h | 60 + video/decode/vd_lavc.c | 857 ++++++++++++ video/filter/pullup.c | 817 ++++++++++++ video/filter/pullup.h | 102 ++ video/filter/vf.c | 830 ++++++++++++ video/filter/vf.h | 189 +++ video/filter/vf_crop.c | 196 +++ video/filter/vf_delogo.c | 379 ++++++ video/filter/vf_divtc.c | 723 +++++++++++ video/filter/vf_dlopen.c | 389 ++++++ video/filter/vf_dlopen.h | 88 ++ video/filter/vf_down3dright.c | 166 +++ video/filter/vf_dsize.c | 125 ++ video/filter/vf_eq2.c | 519 ++++++++ video/filter/vf_expand.c | 350 +++++ video/filter/vf_flip.c | 110 ++ video/filter/vf_format.c | 91 ++ video/filter/vf_gradfun.c | 450 +++++++ video/filter/vf_hqdn3d.c | 373 ++++++ video/filter/vf_ilpack.c | 457 +++++++ video/filter/vf_mirror.c | 131 ++ video/filter/vf_noformat.c | 77 ++ video/filter/vf_noise.c | 470 +++++++ video/filter/vf_phase.c | 303 +++++ video/filter/vf_pp.c | 196 +++ video/filter/vf_pullup.c | 333 +++++ video/filter/vf_rotate.c | 152 +++ video/filter/vf_scale.c | 718 +++++++++++ video/filter/vf_screenshot.c | 219 ++++ video/filter/vf_softpulldown.c | 182 +++ video/filter/vf_stereo3d.c | 527 ++++++++ video/filter/vf_sub.c | 309 +++++ video/filter/vf_swapuv.c | 106 ++ video/filter/vf_unsharp.c | 321 +++++ video/filter/vf_vo.c | 202 +++ video/filter/vf_yadif.c | 533 ++++++++ video/fmt-conversion.c | 144 +++ video/fmt-conversion.h | 27 + video/image_writer.c | 327 +++++ video/image_writer.h | 53 + video/img_format.c | 233 ++++ video/img_format.h | 243 ++++ video/memcpy_pic.h | 77 ++ video/mp_image.c | 280 ++++ video/mp_image.h | 162 +++ video/out/aspect.c | 148 +++ video/out/aspect.h | 37 + video/out/bitmap_packer.c | 227 ++++ video/out/bitmap_packer.h | 68 + video/out/cocoa_common.h | 55 + video/out/cocoa_common.m | 865 +++++++++++++ video/out/d3d_shader_yuv.h | 142 ++ video/out/d3d_shader_yuv.hlsl | 44 + video/out/d3d_shader_yuv_2ch.h | 170 +++ video/out/filter_kernels.c | 279 ++++ video/out/filter_kernels.h | 45 + video/out/geometry.c | 112 ++ video/out/geometry.h | 29 + video/out/gl_common.c | 2654 ++++++++++++++++++++++++++++++++++++++ video/out/gl_common.h | 396 ++++++ video/out/gl_header_fixes.h | 257 ++++ video/out/gl_osd.c | 324 +++++ video/out/gl_osd.h | 43 + video/out/osx_common.c | 145 +++ video/out/osx_common.h | 27 + video/out/pnm_loader.c | 97 ++ video/out/pnm_loader.h | 52 + video/out/vo.c | 530 ++++++++ video/out/vo.h | 352 +++++ video/out/vo_caca.c | 395 ++++++ video/out/vo_corevideo.h | 28 + video/out/vo_corevideo.m | 457 +++++++ video/out/vo_direct3d.c | 2101 ++++++++++++++++++++++++++++++ video/out/vo_image.c | 198 +++ video/out/vo_lavc.c | 553 ++++++++ video/out/vo_null.c | 103 ++ video/out/vo_opengl.c | 2419 ++++++++++++++++++++++++++++++++++ video/out/vo_opengl_old.c | 1166 +++++++++++++++++ video/out/vo_opengl_shaders.glsl | 355 +++++ video/out/vo_vdpau.c | 1718 ++++++++++++++++++++++++ video/out/vo_x11.c | 620 +++++++++ video/out/vo_xv.c | 716 ++++++++++ video/out/w32_common.c | 757 +++++++++++ video/out/w32_common.h | 66 + video/out/x11_common.c | 2404 ++++++++++++++++++++++++++++++++++ video/out/x11_common.h | 184 +++ video/sws_utils.c | 199 +++ video/sws_utils.h | 33 + video/vfcap.h | 46 + 94 files changed, 36526 insertions(+) create mode 100644 video/csputils.c create mode 100644 video/csputils.h create mode 100644 video/decode/dec_video.c create mode 100644 video/decode/dec_video.h create mode 100644 video/decode/vd.c create mode 100644 video/decode/vd.h create mode 100644 video/decode/vd_lavc.c create mode 100644 video/filter/pullup.c create mode 100644 video/filter/pullup.h create mode 100644 video/filter/vf.c create mode 100644 video/filter/vf.h create mode 100644 video/filter/vf_crop.c create mode 100644 video/filter/vf_delogo.c create mode 100644 video/filter/vf_divtc.c create mode 100644 video/filter/vf_dlopen.c create mode 100644 video/filter/vf_dlopen.h create mode 100644 video/filter/vf_down3dright.c create mode 100644 video/filter/vf_dsize.c create mode 100644 video/filter/vf_eq2.c create mode 100644 video/filter/vf_expand.c create mode 100644 video/filter/vf_flip.c create mode 100644 video/filter/vf_format.c create mode 100644 video/filter/vf_gradfun.c create mode 100644 video/filter/vf_hqdn3d.c create mode 100644 video/filter/vf_ilpack.c create mode 100644 video/filter/vf_mirror.c create mode 100644 video/filter/vf_noformat.c create mode 100644 video/filter/vf_noise.c create mode 100644 video/filter/vf_phase.c create mode 100644 video/filter/vf_pp.c create mode 100644 video/filter/vf_pullup.c create mode 100644 video/filter/vf_rotate.c create mode 100644 video/filter/vf_scale.c create mode 100644 video/filter/vf_screenshot.c create mode 100644 video/filter/vf_softpulldown.c create mode 100644 video/filter/vf_stereo3d.c create mode 100644 video/filter/vf_sub.c create mode 100644 video/filter/vf_swapuv.c create mode 100644 video/filter/vf_unsharp.c create mode 100644 video/filter/vf_vo.c create mode 100644 video/filter/vf_yadif.c create mode 100644 video/fmt-conversion.c create mode 100644 video/fmt-conversion.h create mode 100644 video/image_writer.c create mode 100644 video/image_writer.h create mode 100644 video/img_format.c create mode 100644 video/img_format.h create mode 100644 video/memcpy_pic.h create mode 100644 video/mp_image.c create mode 100644 video/mp_image.h create mode 100644 video/out/aspect.c create mode 100644 video/out/aspect.h create mode 100644 video/out/bitmap_packer.c create mode 100644 video/out/bitmap_packer.h create mode 100644 video/out/cocoa_common.h create mode 100644 video/out/cocoa_common.m create mode 100644 video/out/d3d_shader_yuv.h create mode 100644 video/out/d3d_shader_yuv.hlsl create mode 100644 video/out/d3d_shader_yuv_2ch.h create mode 100644 video/out/filter_kernels.c create mode 100644 video/out/filter_kernels.h create mode 100644 video/out/geometry.c create mode 100644 video/out/geometry.h create mode 100644 video/out/gl_common.c create mode 100644 video/out/gl_common.h create mode 100644 video/out/gl_header_fixes.h create mode 100644 video/out/gl_osd.c create mode 100644 video/out/gl_osd.h create mode 100644 video/out/osx_common.c create mode 100644 video/out/osx_common.h create mode 100644 video/out/pnm_loader.c create mode 100644 video/out/pnm_loader.h create mode 100644 video/out/vo.c create mode 100644 video/out/vo.h create mode 100644 video/out/vo_caca.c create mode 100644 video/out/vo_corevideo.h create mode 100644 video/out/vo_corevideo.m create mode 100644 video/out/vo_direct3d.c create mode 100644 video/out/vo_image.c create mode 100644 video/out/vo_lavc.c create mode 100644 video/out/vo_null.c create mode 100644 video/out/vo_opengl.c create mode 100644 video/out/vo_opengl_old.c create mode 100644 video/out/vo_opengl_shaders.glsl create mode 100644 video/out/vo_vdpau.c create mode 100644 video/out/vo_x11.c create mode 100644 video/out/vo_xv.c create mode 100644 video/out/w32_common.c create mode 100644 video/out/w32_common.h create mode 100644 video/out/x11_common.c create mode 100644 video/out/x11_common.h create mode 100644 video/sws_utils.c create mode 100644 video/sws_utils.h create mode 100644 video/vfcap.h (limited to 'video') 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 + * + * 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 +#include +#include +#include + +#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<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 +#include + +#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 +#include +#include +#include + +#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 +#include +#include + +#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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 (