Skip to content

Commit e4cdb23

Browse files
committed
Merge remote-tracking branch 'Muny/develop' into develop
2 parents 478da89 + f5e95d4 commit e4cdb23

File tree

7 files changed

+205
-66
lines changed

7 files changed

+205
-66
lines changed

FlashCap.Core/Internal/BitmapTranscoder.cs

+170-58
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ internal static class BitmapTranscoder
1717
{
1818
private static readonly int scatteringBase = Environment.ProcessorCount;
1919

20+
struct ConversionConstants
21+
{
22+
public int multY, multUB, multUG, multVG, multVR, offsetY;
23+
}
24+
2025
// Prefered article: https://docs.microsoft.com/en-us/windows/win32/medfound/recommended-8-bit-yuv-formats-for-video-rendering#420-formats-16-bits-per-pixel
2126

2227
// some interesting code references:
@@ -25,11 +30,11 @@ internal static class BitmapTranscoder
2530
// TranscodeFormats WITH FullRange suffix means that we suppose Y U and V are in [0..255] range
2631
private static unsafe void TranscodeFromYUVInternal(
2732
int width, int height,
28-
TranscodeFormats conversionStandard, bool isUYVY,
33+
TranscodeFormats conversionStandard, NativeMethods.Compression compression,
2934
byte* pFrom, byte* pTo)
3035
{
3136
// constants for color conversion
32-
int multY, multUB, multUG, multVG, multVR, offsetY;
37+
ConversionConstants conversionConstants;
3338

3439
// select constants for the color conversion
3540
switch (conversionStandard)
@@ -52,21 +57,28 @@ private static unsafe void TranscodeFromYUVInternal(
5257

5358
// multiply Y by 1.16438
5459
// multiply UV by 1.13839
55-
multY = 298;
56-
multUB = 587;
57-
multUG = 114;
58-
multVG = 237;
59-
multVR = 466;
60-
offsetY = 16;
60+
conversionConstants = new ConversionConstants
61+
{
62+
multY = 298,
63+
multUB = 587,
64+
multUG = 114,
65+
multVG = 237,
66+
multVR = 466,
67+
offsetY = 16
68+
};
6169
break;
6270

6371
case TranscodeFormats.BT601FullRange:
64-
multY = 255;
65-
multUB = 516;
66-
multUG = 100;
67-
multVG = 208;
68-
multVR = 409;
69-
offsetY = 0;
72+
73+
conversionConstants = new ConversionConstants
74+
{
75+
multY = 255,
76+
multUB = 516,
77+
multUG = 100,
78+
multVG = 208,
79+
multVR = 409,
80+
offsetY = 0
81+
};
7082
break;
7183

7284
//////////////////////////////////////////////////
@@ -87,21 +99,28 @@ private static unsafe void TranscodeFromYUVInternal(
8799

88100
// multiply Y by 1.16438
89101
// multiply UV by 1.13839
90-
multY = 298;
91-
multUB = 541;
92-
multUG = 55;
93-
multVG = 137;
94-
multVR = 459;
95-
offsetY = 16;
102+
conversionConstants = new ConversionConstants
103+
{
104+
multY = 298,
105+
multUB = 541,
106+
multUG = 55,
107+
multVG = 137,
108+
multVR = 459,
109+
offsetY = 16
110+
};
96111
break;
97112

98113
case TranscodeFormats.BT709FullRange:
99-
multY = 255;
100-
multUB = 475;
101-
multUG = 48;
102-
multVG = 120;
103-
multVR = 403;
104-
offsetY = 0;
114+
115+
conversionConstants = new ConversionConstants
116+
{
117+
multY = 255,
118+
multUB = 475,
119+
multUG = 48,
120+
multVG = 120,
121+
multVR = 403,
122+
offsetY = 0
123+
};
105124
break;
106125

107126
//////////////////////////////////////////////////
@@ -113,21 +132,29 @@ private static unsafe void TranscodeFromYUVInternal(
113132

114133
// multiply Y by 1.16438
115134
// multiply UV by 1.13839
116-
multY = 298;
117-
multUB = 549;
118-
multUG = 48;
119-
multVG = 166;
120-
multVR = 429;
121-
offsetY = 16;
135+
136+
conversionConstants = new ConversionConstants
137+
{
138+
multY = 298,
139+
multUB = 549,
140+
multUG = 48,
141+
multVG = 166,
142+
multVR = 429,
143+
offsetY = 16
144+
};
122145
break;
123146

124147
case TranscodeFormats.BT2020FullRange:
125-
multY = 255;
126-
multUB = 482;
127-
multUG = 42;
128-
multVG = 146;
129-
multVR = 377;
130-
offsetY = 0;
148+
149+
conversionConstants = new ConversionConstants
150+
{
151+
multY = 255,
152+
multUB = 482,
153+
multUG = 42,
154+
multVG = 146,
155+
multVR = 377,
156+
offsetY = 0
157+
};
131158
break;
132159

133160
//////////////////////////////////////////////////
@@ -136,6 +163,88 @@ private static unsafe void TranscodeFromYUVInternal(
136163
throw new ArgumentException(nameof(conversionStandard));
137164
}
138165

166+
switch(compression)
167+
{
168+
case NativeMethods.Compression.UYVY:
169+
case NativeMethods.Compression.HDYC:
170+
YUY2_UYVY_to_RGB24(true, width, height, conversionConstants, compression, pFrom, pTo);
171+
break;
172+
case NativeMethods.Compression.YUYV:
173+
case NativeMethods.Compression.YUY2:
174+
YUY2_UYVY_to_RGB24(false, width, height, conversionConstants, compression, pFrom, pTo);
175+
break;
176+
case NativeMethods.Compression.NV12:
177+
NV12_to_RGB24(width, height, conversionConstants, pFrom, pTo);
178+
break;
179+
default:
180+
throw new ArgumentException(nameof(compression));
181+
}
182+
}
183+
184+
private static unsafe void NV12_to_RGB24(
185+
int width, int height,
186+
ConversionConstants cconsts,
187+
byte* pFrom, byte* pTo)
188+
{
189+
var scatter = height / scatteringBase;
190+
Parallel.For(0, (height + scatter - 1) / scatter, ys =>
191+
{
192+
var y = ys * scatter;
193+
var myi = Math.Min(height - y, scatter);
194+
195+
for (var yi = 0; yi < myi; yi++)
196+
{
197+
byte* pFromY = pFrom + (y + yi) * width;
198+
byte* pFromUV = pFrom + (height + (y + yi) / 2) * width;
199+
200+
byte* pToBase = pTo + (height - (y + yi) - 1) * width * 3;
201+
202+
203+
for (var x = 0; x < width - 1; x += 2)
204+
{
205+
int c1 = pFromY[0] - cconsts.offsetY; // Y1
206+
int c2 = pFromY[1] - cconsts.offsetY; // Y2
207+
int d = pFromUV[0] - 128; // U
208+
int e = pFromUV[1] - 128; // V
209+
210+
int cc1 = cconsts.multY * c1;
211+
int cc2 = cconsts.multY * c2;
212+
213+
*pToBase++ = Clip((cc1 + cconsts.multUB * d + 128) >> 8); // B1
214+
*pToBase++ = Clip((cc1 - cconsts.multUG * d - cconsts.multVG * e + 128) >> 8); // G1
215+
*pToBase++ = Clip((cc1 + cconsts.multVR * e + 128) >> 8); // R1
216+
217+
*pToBase++ = Clip((cc2 + cconsts.multUB * d + 128) >> 8); // B2
218+
*pToBase++ = Clip((cc2 - cconsts.multUG * d - cconsts.multVG * e + 128) >> 8); // G2
219+
*pToBase++ = Clip((cc2 + cconsts.multVR * e + 128) >> 8); // R2
220+
221+
pFromY += 2;
222+
pFromUV += 2;
223+
}
224+
225+
if ((width & 1) != 0)
226+
{
227+
int c1 = pFromY[0] - cconsts.offsetY; // Y1
228+
int d = pFromUV[0] - 128; // U
229+
int e = pFromUV[1] - 128; // V
230+
231+
int cc1 = cconsts.multY * c1;
232+
233+
*pToBase++ = Clip((cc1 + cconsts.multUB * d + 128) >> 8); // B1
234+
*pToBase++ = Clip((cc1 - cconsts.multUG * d - cconsts.multVG * e + 128) >> 8); // G1
235+
*pToBase++ = Clip((cc1 + cconsts.multVR * e + 128) >> 8); // R1
236+
}
237+
}
238+
});
239+
}
240+
241+
private static unsafe void YUY2_UYVY_to_RGB24(
242+
bool isUYVY,
243+
int width, int height,
244+
ConversionConstants cconsts,
245+
NativeMethods.Compression compression,
246+
byte* pFrom, byte* pTo)
247+
{
139248
var scatter = height / scatteringBase;
140249
Parallel.For(0, (height + scatter - 1) / scatter, ys =>
141250
{
@@ -150,31 +259,33 @@ private static unsafe void TranscodeFromYUVInternal(
150259

151260
for (var x = 0; x < width; x += 2)
152261
{
262+
// UYVY, HDYC
153263
if (isUYVY)
154264
{
155265
d = pFromBase[0] - 128; // U
156-
c1 = pFromBase[1] - offsetY; // Y1
266+
c1 = pFromBase[1] - cconsts.offsetY; // Y1
157267
e = pFromBase[2] - 128; // V
158-
c2 = pFromBase[3] - offsetY; // Y2
268+
c2 = pFromBase[3] - cconsts.offsetY; // Y2
159269
}
270+
// YUY2, YUYV
160271
else
161272
{
162-
c1 = pFromBase[0] - offsetY; // Y1
273+
c1 = pFromBase[0] - cconsts.offsetY; // Y1
163274
d = pFromBase[1] - 128; // U
164-
c2 = pFromBase[2] - offsetY; // Y2
275+
c2 = pFromBase[2] - cconsts.offsetY; // Y2
165276
e = pFromBase[3] - 128; // V
166277
}
167278

168-
cc1 = multY * c1;
169-
cc2 = multY * c2;
279+
cc1 = cconsts.multY * c1;
280+
cc2 = cconsts.multY * c2;
170281

171-
*pToBase++ = Clip((cc1 + multUB * d + 128) >> 8); // B1
172-
*pToBase++ = Clip((cc1 - multUG * d - multVG * e + 128) >> 8); // G1
173-
*pToBase++ = Clip((cc1 + multVR * e + 128) >> 8); // R1
282+
*pToBase++ = Clip((cc1 + cconsts.multUB * d + 128) >> 8); // B1
283+
*pToBase++ = Clip((cc1 - cconsts.multUG * d - cconsts.multVG * e + 128) >> 8); // G1
284+
*pToBase++ = Clip((cc1 + cconsts.multVR * e + 128) >> 8); // R1
174285

175-
*pToBase++ = Clip((cc2 + multUB * d + 128) >> 8); // B2
176-
*pToBase++ = Clip((cc2 - multUG * d - multVG * e + 128) >> 8); // G2
177-
*pToBase++ = Clip((cc2 + multVR * e + 128) >> 8); // R2
286+
*pToBase++ = Clip((cc2 + cconsts.multUB * d + 128) >> 8); // B2
287+
*pToBase++ = Clip((cc2 - cconsts.multUG * d - cconsts.multVG * e + 128) >> 8); // G2
288+
*pToBase++ = Clip((cc2 + cconsts.multVR * e + 128) >> 8); // R2
178289

179290
pFromBase += 4;
180291
}
@@ -184,7 +295,8 @@ private static unsafe void TranscodeFromYUVInternal(
184295

185296
private static unsafe void TranscodeFromYUV(
186297
int width, int height,
187-
TranscodeFormats transcodeFormat, bool isUYVY,
298+
TranscodeFormats transcodeFormat,
299+
NativeMethods.Compression compression,
188300
byte* pFrom, byte* pTo)
189301
{
190302
switch (transcodeFormat)
@@ -195,16 +307,16 @@ private static unsafe void TranscodeFromYUV(
195307
case TranscodeFormats.BT709FullRange:
196308
case TranscodeFormats.BT2020:
197309
case TranscodeFormats.BT2020FullRange:
198-
TranscodeFromYUVInternal(width, height, transcodeFormat, isUYVY, pFrom, pTo);
310+
TranscodeFromYUVInternal(width, height, transcodeFormat, compression, pFrom, pTo);
199311
break;
200312
case TranscodeFormats.Auto:
201313
// determine the color conversion based on the width and height of the frame
202314
if (width > 1920 || height > 1080) // UHD or larger
203-
TranscodeFromYUVInternal(width, height, TranscodeFormats.BT2020, isUYVY, pFrom, pTo);
315+
TranscodeFromYUVInternal(width, height, TranscodeFormats.BT2020, compression, pFrom, pTo);
204316
else if (width > 720 || height > 576) // HD
205-
TranscodeFromYUVInternal(width, height, TranscodeFormats.BT709, isUYVY, pFrom, pTo);
317+
TranscodeFromYUVInternal(width, height, TranscodeFormats.BT709, compression, pFrom, pTo);
206318
else // SD
207-
TranscodeFromYUVInternal(width, height, TranscodeFormats.BT601, isUYVY, pFrom, pTo);
319+
TranscodeFromYUVInternal(width, height, TranscodeFormats.BT601, compression, pFrom, pTo);
208320
break;
209321
default:
210322
throw new ArgumentException(nameof(transcodeFormat));
@@ -228,6 +340,7 @@ private static byte Clip(int value) =>
228340
case NativeMethods.Compression.YUYV:
229341
case NativeMethods.Compression.YUY2:
230342
case NativeMethods.Compression.HDYC:
343+
case NativeMethods.Compression.NV12:
231344
return width * height * 3;
232345
default:
233346
return null;
@@ -244,11 +357,10 @@ public static unsafe void Transcode(
244357
{
245358
case NativeMethods.Compression.UYVY:
246359
case NativeMethods.Compression.HDYC:
247-
TranscodeFromYUV(width, height, transcodeFormat, true, pFrom, pTo);
248-
break;
249360
case NativeMethods.Compression.YUYV:
250361
case NativeMethods.Compression.YUY2:
251-
TranscodeFromYUV(width, height, transcodeFormat, false, pFrom, pTo);
362+
case NativeMethods.Compression.NV12:
363+
TranscodeFromYUV(width, height, transcodeFormat, compression, pFrom, pTo);
252364
break;
253365
default:
254366
throw new ArgumentException(nameof(compression));

FlashCap.Core/Internal/NativeMethods.cs

+7-1
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,8 @@ public enum Compression
199199
YUYV = 0x56595559, // FOURCC
200200
UYVY = 0x59565955, // FOURCC
201201
MJPG = 0x47504A4D, // FOURCC
202-
HDYC = 0x43594448 // FOURCC (BlackMagic input (UYVY))
202+
HDYC = 0x43594448, // FOURCC (BlackMagic input (UYVY))
203+
NV12 = 0x3231564E, // FOURCC
203204
}
204205

205206
private static int CalculateClrUsed(
@@ -506,6 +507,7 @@ static PixelFormats GetRGBPixelFormat(int clrBits) =>
506507
Compression.YUYV => PixelFormats.YUYV,
507508
Compression.YUY2 => PixelFormats.YUYV,
508509
Compression.HDYC => PixelFormats.YUYV,
510+
Compression.NV12 => PixelFormats.NV12,
509511
_ => PixelFormats.Unknown,
510512
};
511513

@@ -579,6 +581,10 @@ public static bool GetCompressionAndBitCount(
579581
compression = Compression.YUYV;
580582
bitCount = 16;
581583
return true;
584+
case PixelFormats.NV12:
585+
compression = Compression.NV12;
586+
bitCount = 12;
587+
return true;
582588
default:
583589
compression = default;
584590
bitCount = 0;

FlashCap.Core/Internal/NativeMethods_V4L2.cs

+3
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ static NativeMethods_V4L2()
9191
pixelFormats.Add(Interop.V4L2_PIX_FMT_UYVY, PixelFormats.UYVY);
9292
pixelFormats.Add(Interop.V4L2_PIX_FMT_YUYV, PixelFormats.YUYV);
9393
pixelFormats.Add(Interop.V4L2_PIX_FMT_YUY2, PixelFormats.YUYV);
94+
pixelFormats.Add(Interop.V4L2_PIX_FMT_NV12, PixelFormats.NV12);
9495
}
9596

9697
public static bool IsKnownPixelFormat(uint pix_fmt) =>
@@ -324,6 +325,8 @@ public static uint[] GetPixelFormats(
324325
return new[] { Interop.V4L2_PIX_FMT_UYVY };
325326
case PixelFormats.YUYV:
326327
return new[] { Interop.V4L2_PIX_FMT_YUYV, Interop.V4L2_PIX_FMT_YUY2 };
328+
case PixelFormats.NV12:
329+
return new[] { Interop.V4L2_PIX_FMT_NV12 };
327330
case PixelFormats.JPEG:
328331
return new[] { Interop.V4L2_PIX_FMT_MJPEG, Interop.V4L2_PIX_FMT_JPEG, (uint)NativeMethods.Compression.BI_JPEG };
329332
case PixelFormats.PNG:

0 commit comments

Comments
 (0)