1 /*
2  * Copyright (C) 2012-2019 the xine project
3  *
4  * This file is part of xine, a free video player.
5  *
6  * xine is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * xine is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19  *
20  */
21 
22 
23 /*
24   TJ. the output color matrix selection feature.
25   Example use:
26 */
27 #if 0
28   #define CM_LUT /* recommended optimization */
29 
30   typedef struct {
31     ...
32     int     cm_state;
33   #ifdef CM_LUT
34     uint8_t cm_lut[32];
35   #endif
36     ...
37   } xxxx_driver_t;
38 
39   #define CM_HAVE_YCGCO_SUPPORT /* if you already handle that */
40   #define CM_DRIVER_T xxxx_driver_t
41   #include "color_matrix.c"
42 #endif
43 /*
44   cm_from_frame () returns current (color_matrix << 1) | color_range control value.
45   Having only 1 var simplifies change event handling and avoids unecessary vo
46   reconfiguration. In the libyuv2rgb case, they are even handled by same code.
47 
48   In theory, HD video uses a different YUV->RGB matrix than the rest.
49   It shall come closer to the human eye's brightness feel, and give
50   more shades of green even without higher bit depth.
51 
52   I discussed this topic with local TV engineers earlier.
53   They say their studio equipment throws around uncompressed YUV with no
54   extra info attached to it. Anything smaller than 720p is assumed to be
55   ITU-R 601, otherwise ITU-R 709. A rematrix filter applies whenever
56   video is scaled across the above mentioned HD threshold.
57 
58   However, the weak point of their argumentation is potentially non-standard
59   input material. Those machines obviously dont verify input data, and
60   ocasionally they dont even respect stream info (tested by comparing TV
61   against retail DVD version of same movie).
62 
63   Consumer TV sets handle this fairly inconsistent - stream info, video size,
64   hard-wired matrix, user choice or so-called intelligent picture enhancers
65   that effectively go way off standards.
66   So I decided to provide functionality, and let the user decide if and how
67   to actually use it.
68 */
69 
70 /* eveybody gets these */
71 
72 /* user configuration settings */
73 #define CM_CONFIG_NAME   "video.output.color_matrix"
74 #define CM_CONFIG_SIGNAL 0
75 #define CM_CONFIG_SIZE   1
76 #define CM_CONFIG_SD     2
77 #define CM_CONFIG_HD     3
78 
79 #define CR_CONFIG_NAME   "video.output.color_range"
80 #define CR_CONFIG_AUTO   0
81 #define CR_CONFIG_MPEG   1
82 #define CR_CONFIG_FULL   2
83 
84 static const char * const cm_names[] = {
85   "RGB",
86   "RGB",
87   "ITU-R 709 / HDTV",
88   "full range ITU-R 709 / HDTV",
89   "undefined",
90   "full range, undefined",
91   "ITU-R 470 BG / SDTV",
92   "full range ITU-R 470 BG / SDTV",
93   "FCC",
94   "full range FCC",
95   "ITU-R 470 BG / SDTV",
96   "full range ITU-R 470 BG / SDTV",
97   "SMPTE 170M",
98   "full range SMPTE 170M",
99   "SMPTE 240M",
100   "full range SMPTE 240M"
101 #ifdef CM_HAVE_YCGCO_SUPPORT
102   ,
103   "YCgCo",
104   "YCgCo", /* this is always fullrange */
105   "#9",
106   "fullrange #9",
107   "#10",
108   "fullrange #10",
109   "#11",
110   "fullrange #11",
111   "#12",
112   "fullrange #12",
113   "#13",
114   "fullrange #13",
115   "#14",
116   "fullrange #14",
117   "#15",
118   "fullrange #15"
119 #endif
120 };
121 
122 #ifdef CM_DRIVER_T
123 
124 /* this is for vo plugins only */
125 
126 /* the option names */
127 static const char * const cm_conf_labels[] = {
128   "Signal", "Signal+Size", "SD", "HD", NULL
129 };
130 
131 static const char * const cr_conf_labels[] = {
132   "Auto", "MPEG", "FULL", NULL
133 };
134 
135 #ifdef CM_HAVE_YCGCO_SUPPORT
136 #  define CM_G 16
137 #else
138 #  define CM_G 10
139 #endif
140 
141 static
142 #ifdef CM_LUT
143 const
144 #endif
145 uint8_t cm_m[] = {
146   10, 2,10, 6, 8,10,12,14,CM_G,10,10,10,10,10,10,10, /* SIGNAL */
147   10, 2, 0, 6, 8,10,12,14,CM_G,10,10,10,10,10,10,10, /* SIZE */
148   10,10,10,10,10,10,10,10,CM_G,10,10,10,10,10,10,10, /* SD */
149   10, 2, 2, 2, 2, 2, 2, 2,CM_G, 2, 2, 2, 2, 2, 2, 2  /* HD */
150 };
151 
cm_lut_setup(CM_DRIVER_T * this)152 static void cm_lut_setup (CM_DRIVER_T *this) {
153 #ifdef CM_LUT
154   {
155     const uint8_t *a = cm_m + ((this->cm_state >> 2) << 4);
156     uint8_t *d = this->cm_lut, *e = d + 32;
157     while (d < e) {
158       d[0] = d[1] = *a++;
159       d += 2;
160     }
161   }
162   if ((this->cm_state & 3) == CR_CONFIG_AUTO) {
163     /* keep range */
164     int i;
165     for (i = 1; i < 32; i += 2)
166       this->cm_lut[i] |= 1;
167   } else if ((this->cm_state & 3) == CR_CONFIG_FULL) {
168     /* force full range */
169     int i;
170     for (i = 0; i < 32; i += 1)
171       this->cm_lut[i] |= 1;
172   }
173 #endif
174 }
175 
176 /* callback when user changes them */
cm_cb_config(void * this_gen,xine_cfg_entry_t * entry)177 static void cm_cb_config (void *this_gen, xine_cfg_entry_t *entry) {
178   CM_DRIVER_T *this = (CM_DRIVER_T *)this_gen;
179   this->cm_state = (this->cm_state & 3) | (entry->num_value << 2);
180   cm_lut_setup (this);
181 }
182 
cr_cb_config(void * this_gen,xine_cfg_entry_t * entry)183 static void cr_cb_config (void *this_gen, xine_cfg_entry_t *entry) {
184   CM_DRIVER_T *this = (CM_DRIVER_T *)this_gen;
185   this->cm_state = (this->cm_state & 0x1c) | entry->num_value;
186   cm_lut_setup (this);
187 }
188 
cm_init(CM_DRIVER_T * this)189 static void cm_init (CM_DRIVER_T *this) {
190   /* register configuration */
191   this->cm_state = this->xine->config->register_enum (
192     this->xine->config,
193     CM_CONFIG_NAME,
194     CM_CONFIG_SIZE,
195     (char **)cm_conf_labels,
196     _("Output colour matrix"),
197     _("Tell how output colours should be calculated.\n\n"
198       "Signal: Do as current stream suggests.\n"
199       "        This may be wrong sometimes.\n\n"
200       "Signal+Size: Same as above,\n"
201       "        but assume HD colour for unmarked HD streams.\n\n"
202       "SD:     Force SD video standard ITU-R 470/601.\n"
203       "        Try this if you get too little green.\n\n"
204       "HD:     Force HD video standard ITU-R 709.\n"
205       "        Try when there is too much green coming out.\n\n"),
206     10,
207     cm_cb_config,
208     this
209   ) << 2;
210   this->cm_state |= this->xine->config->register_enum (
211     this->xine->config,
212     CR_CONFIG_NAME,
213     CR_CONFIG_AUTO,
214     (char **)cr_conf_labels,
215     _("Output colour range"),
216     _("Tell how output colours should be ranged.\n\n"
217       "Auto: Do as current stream suggests.\n"
218       "      This may be wrong sometimes.\n\n"
219       "MPEG: Force MPEG colour range (16..235) / studio swing / video mode.\n"
220       "      Try if image looks dull (no real black or white in it).\n\n"
221       "FULL: Force FULL colour range (0..255) / full swing / PC mode.\n"
222       "      Try when flat black and white spots appear.\n\n"),
223     10,
224     cr_cb_config,
225     this
226   );
227   cm_lut_setup (this);
228 }
229 
cm_from_frame(vo_frame_t * frame)230 static int cm_from_frame (vo_frame_t *frame) {
231   CM_DRIVER_T *this = (CM_DRIVER_T *)frame->driver;
232   int cm = VO_GET_FLAGS_CM (frame->flags);
233 #ifdef CM_LUT
234   cm = this->cm_lut[cm & 31];
235   if (cm & ~1)
236     return cm;
237   return cm | ((frame->height - frame->crop_top - frame->crop_bottom >= 720) ||
238                (frame->width - frame->crop_left - frame->crop_right >= 1280) ? 2 : 10);
239 #else
240   static uint8_t cm_r[] = {0, 0, 1, 0}; /* AUTO, MPEG, FULL, safety */
241   int cf = this->cm_state;
242   cm_m[18] = (frame->height - frame->crop_top - frame->crop_bottom >= 720) ||
243              (frame->width - frame->crop_left - frame->crop_right >= 1280) ? 2 : 10;
244   cm_r[0] = cm & 1;
245   return cm_m[((cf >> 2) << 4) | (cm >> 1)] | cm_r[cf & 3];
246 #endif
247 }
248 
cm_close(CM_DRIVER_T * this)249 static void cm_close (CM_DRIVER_T *this) {
250   /* dont know whether this is really necessary */
251   this->xine->config->unregister_callbacks (this->xine->config, NULL, NULL, this, sizeof (*this));
252 }
253 
254 #endif /* defined CM_DRIVER_T */
255