]> git.sesse.net Git - movit/blob - ycbcr_422interleaved_input.h
Fix more confusion with strong one-to-one effects and compute shaders.
[movit] / ycbcr_422interleaved_input.h
1 #ifndef _MOVIT_YCBCR_422INTERLEAVED_INPUT_H
2 #define _MOVIT_YCBCR_422INTERLEAVED_INPUT_H 1
3
4 // YCbCr422InterleavedInput is for handling 4:2:2 interleaved 8-bit Y'CbCr,
5 // which you can get from e.g. certain capture cards. (Most other Y'CbCr
6 // encodings are planar, which is handled by YCbCrInput.) Currently we only
7 // handle the UYVY variant, although YUY2 should be easy to support if needed.
8 //
9 // Horizontal chroma placement is freely choosable as with YCbCrInput,
10 // but BT.601 (which at least DeckLink claims to conform to, under the
11 // name CCIR 601) seems to specify chroma positioning to the far left
12 // (that is 0.0); BT.601 Annex 1 (page 7) says “C R and C B samples co-sited
13 // with odd (1st, 3rd, 5th, etc.) Y samples in each line”, and I assume they do
14 // not start counting from 0 when they use the “1st” moniker.
15 //
16 // Interpolation is bilinear as in YCbCrInput (done by the GPU's normal
17 // scaling, except for the Y channel which of course needs some fiddling),
18 // and is done in non-linear light (since that's what everything specifies,
19 // except Rec. 2020 lets you choose between the two). A higher-quality
20 // choice would be to use a single pass of ResampleEffect to scale the
21 // chroma, but for now we are consistent between the two.
22 //
23 // There is a disparity between the interleaving and the way OpenGL typically
24 // expects to sample. In lieu of accessible hardware support (a lot of hardware
25 // supports native interleaved 4:2:2 sampling, but OpenGL drivers seem to
26 // rarely support it), we simply upload the same data twice; once as a
27 // full-width RG texture (from which we sample luma) and once as a half-width
28 // RGBA texture (from which we sample chroma). We throw away half of the color
29 // channels each time, so bandwidth is wasted, but it makes for a very
30 // uncomplicated shader.
31 //
32 // Note that if you can shuffle your data around very cheaply on the CPU
33 // (say, while you're decoding it out of some other buffer anyway),
34 // regular YCbCrInput with YCBCR_INPUT_SPLIT_Y_AND_CBCR will probably be
35 // more efficient, as it doesn't need this bandwidth waste.
36
37 #include <epoxy/gl.h>
38 #include <string>
39
40 #include "effect.h"
41 #include "effect_chain.h"
42 #include "image_format.h"
43 #include "input.h"
44 #include "ycbcr.h"
45
46 namespace movit {
47
48 class ResourcePool;
49
50 class YCbCr422InterleavedInput : public Input {
51 public:
52         // <ycbcr_format> must be consistent with 4:2:2 sampling; specifically:
53         //
54         //  * chroma_subsampling_x must be 2.
55         //  * chroma_subsampling_y must be 1.
56         //
57         // <width> must obviously be an even number. It is the true width of the image
58         // in pixels, ie., the number of horizontal luma samples.
59         YCbCr422InterleavedInput(const ImageFormat &image_format,
60                                  const YCbCrFormat &ycbcr_format,
61                                  unsigned width, unsigned height);
62         ~YCbCr422InterleavedInput();
63
64         std::string effect_type_id() const override { return "YCbCr422InterleavedInput"; }
65
66         bool can_output_linear_gamma() const override { return false; }
67         AlphaHandling alpha_handling() const override { return OUTPUT_BLANK_ALPHA; }
68
69         std::string output_fragment_shader() override;
70
71         // Uploads the texture if it has changed since last time.
72         void set_gl_state(GLuint glsl_program_num, const std::string& prefix, unsigned *sampler_num) override;
73
74         unsigned get_width() const override { return width; }
75         unsigned get_height() const override { return height; }
76         Colorspace get_color_space() const override { return image_format.color_space; }
77         GammaCurve get_gamma_curve() const override { return image_format.gamma_curve; }
78         bool can_supply_mipmaps() const override { return false; }
79
80         // Tells the input where to fetch the actual pixel data. Note that if you change
81         // this data, you must either call set_pixel_data() again (using the same pointer
82         // is fine), or invalidate_pixel_data(). Otherwise, the texture won't be re-uploaded
83         // on subsequent frames.
84         //
85         // The data can either be a regular pointer (if pbo==0), or a byte offset
86         // into a PBO. The latter will allow you to start uploading the texture data
87         // asynchronously to the GPU, if you have any CPU-intensive work between the
88         // call to set_pixel_data() and the actual rendering. Also, since we upload
89         // the data twice, using a PBO can save texture upload bandwidth. In either case,
90         // the pointer (and PBO, if set) has to be valid at the time of the render call.
91         void set_pixel_data(const unsigned char *pixel_data, GLuint pbo = 0)
92         {
93                 this->pixel_data = pixel_data;
94                 this->pbo = pbo;
95                 invalidate_pixel_data();
96         }
97
98         void invalidate_pixel_data();
99
100         void set_pitch(unsigned pitch)
101         {
102                 assert(pitch % ycbcr_format.chroma_subsampling_x == 0);
103                 pitches[CHANNEL_LUMA] = pitch;
104                 pitches[CHANNEL_CHROMA] = pitch / ycbcr_format.chroma_subsampling_x;
105                 invalidate_pixel_data();
106         }
107
108         void inform_added(EffectChain *chain) override
109         {
110                 resource_pool = chain->get_resource_pool();
111         }
112
113         bool set_int(const std::string& key, int value) override;
114
115 private:
116         ImageFormat image_format;
117         YCbCrFormat ycbcr_format;
118         GLuint pbo;
119
120         // Luma texture is 0, chroma texture is 1.
121         enum Channel {
122                 CHANNEL_LUMA,
123                 CHANNEL_CHROMA
124         };
125         GLuint texture_num[2];
126         GLuint widths[2];
127         unsigned pitches[2];
128
129         unsigned width, height;
130         const unsigned char *pixel_data;
131         ResourcePool *resource_pool;
132
133         GLint uniform_tex_y, uniform_tex_cbcr;
134 };
135
136 }  // namespace movit
137
138 #endif  // !defined(_MOVIT_YCBCR_422INTERLEAVED_INPUT_H)