Skip to content

Commit 37ebbd7

Browse files
committed
Significantly improved color data transfer util and its printed info
String-based properties now get auto-converted to their integer (e.g. bt709 > 1 or bt2020 > 9), and VideoColorData.ToString() now shows the string version of each property too instead of just the number
1 parent b43c077 commit 37ebbd7

File tree

3 files changed

+227
-68
lines changed

3 files changed

+227
-68
lines changed
Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System;
1+
using Nmkoder.Utils;
2+
using System;
23
using System.Collections.Generic;
34
using System.Drawing;
45
using System.Linq;
@@ -9,10 +10,10 @@ namespace Nmkoder.Data
910
{
1011
public class VideoColorData
1112
{
12-
public string ColorTransfer { get; set; } = "";
13-
public string ColorMatrixCoeffs { get; set; } = "";
14-
public string ColorPrimaries { get; set; } = "";
15-
public string ColorRange { get; set; } = "";
13+
public int ColorTransfer { get; set; } = 2;
14+
public int ColorMatrixCoeffs { get; set; } = 2;
15+
public int ColorPrimaries { get; set; } = 2;
16+
public int ColorRange { get; set; } = 0;
1617
public string RedX { get; set; } = "";
1718
public string RedY { get; set; } = "";
1819
public string GreenX { get; set; } = "";
@@ -26,21 +27,40 @@ public class VideoColorData
2627

2728
public override string ToString()
2829
{
29-
return
30-
$"Color transfer: {ColorTransfer}" +
31-
$"\nColor matrix coefficients: {ColorMatrixCoeffs}" +
32-
$"\nColor primaries: {ColorPrimaries}" +
33-
$"\nColor range: {ColorRange}" +
34-
$"\nRed color coordinate x: {RedX}" +
35-
$"\nRed color coordinate y: {RedY}" +
36-
$"\nGreen color coordinate x: {GreenX}" +
37-
$"\nGreen color coordinate y: {GreenY}" +
38-
$"\nBlue color coordinate y: {BlueX}" +
39-
$"\nBlue color coordinate x: {BlueY}" +
40-
$"\nWhite color coordinate y: {WhiteX}" +
41-
$"\nWhite color coordinate x: {WhiteY}" +
42-
$"\nMaximum luminance: {LumaMax}" +
43-
$"\nMinimum luminance: {LumaMin}";
30+
//return
31+
//$"Color transfer: {ColorTransfer}" +
32+
//$"\nColor matrix coefficients: {ColorMatrixCoeffs}" +
33+
//$"\nColor primaries: {ColorPrimaries}" +
34+
//$"\nColor range: {ColorRange}" +
35+
//$"\nRed color coordinate x: {RedX}" +
36+
//$"\nRed color coordinate y: {RedY}" +
37+
//$"\nGreen color coordinate x: {GreenX}" +
38+
//$"\nGreen color coordinate y: {GreenY}" +
39+
//$"\nBlue color coordinate y: {BlueX}" +
40+
//$"\nBlue color coordinate x: {BlueY}" +
41+
//$"\nWhite color coordinate y: {WhiteX}" +
42+
//$"\nWhite color coordinate x: {WhiteY}" +
43+
//$"\nMaximum luminance: {LumaMax}" +
44+
//$"\nMinimum luminance: {LumaMin}";
45+
46+
List<string> lines = new List<string>();
47+
48+
try
49+
{
50+
lines.Add($"Color transfer: {ColorTransfer} ({ColorDataUtils.GetColorTransferName(ColorTransfer)})");
51+
lines.Add($"Colour matrix coefficients: {ColorMatrixCoeffs} ({ColorDataUtils.GetColorMatrixCoeffsName(ColorMatrixCoeffs)})");
52+
lines.Add($"Colour primaries: {ColorPrimaries} ({ColorDataUtils.GetColorPrimariesName(ColorPrimaries)})");
53+
lines.Add($"Colour range: {ColorRange} ({ColorDataUtils.GetColorRangeName(ColorRange)})");
54+
if (!string.IsNullOrWhiteSpace(RedX) && !string.IsNullOrWhiteSpace(RedY)) lines.Add($"Red color coordinates X/Y: {RedX}/{RedY}");
55+
if (!string.IsNullOrWhiteSpace(GreenX) && !string.IsNullOrWhiteSpace(GreenY)) lines.Add($"Green color coordinates X/Y: {GreenX}/{GreenY}");
56+
if (!string.IsNullOrWhiteSpace(BlueX) && !string.IsNullOrWhiteSpace(BlueY)) lines.Add($"Blue color coordinates X/Y: {BlueX}/{BlueY}");
57+
if (!string.IsNullOrWhiteSpace(WhiteX) && !string.IsNullOrWhiteSpace(WhiteY)) lines.Add($"White color coordinates X/Y: {WhiteX}/{WhiteY}");
58+
if (!string.IsNullOrWhiteSpace(LumaMin)) lines.Add($"Minimum luminance: {LumaMin}");
59+
if (!string.IsNullOrWhiteSpace(LumaMax)) lines.Add($"Maximum luminance: {LumaMax}");
60+
}
61+
catch { }
62+
63+
return string.Join("\n", lines);
4464
}
4565
}
4666
}

ff-utils-winforms/UI/Tasks/UtilColorData.cs

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,22 +39,7 @@ public static async Task Run()
3939
{
4040
Logger.Log($"Only one file loaded - Will only print metadata for {Path.GetFileName(vidSrc)}.");
4141
VideoColorData d = await ColorDataUtils.GetColorData(vidSrc);
42-
List<string> lines = new List<string>();
43-
if (!string.IsNullOrWhiteSpace(d.ColorTransfer)) lines.Add($"Color transfer: {d.ColorTransfer}");
44-
if (!string.IsNullOrWhiteSpace(d.ColorMatrixCoeffs)) lines.Add($"Colour matrix coefficients: {d.ColorMatrixCoeffs}");
45-
if (!string.IsNullOrWhiteSpace(d.ColorMatrixCoeffs)) lines.Add($"Colour primaries: {d.ColorPrimaries}");
46-
if (!string.IsNullOrWhiteSpace(d.ColorRange)) lines.Add($"Colour range: {d.ColorRange}");
47-
if (!string.IsNullOrWhiteSpace(d.RedX) && !string.IsNullOrWhiteSpace(d.RedY)) lines.Add($"Red color coordinates X/Y: {d.RedX}/{d.RedY}");
48-
if (!string.IsNullOrWhiteSpace(d.GreenX) && !string.IsNullOrWhiteSpace(d.GreenY)) lines.Add($"Green color coordinates X/Y: {d.GreenX}/{d.GreenY}");
49-
if (!string.IsNullOrWhiteSpace(d.BlueX) && !string.IsNullOrWhiteSpace(d.BlueY)) lines.Add($"Blue color coordinates X/Y: {d.BlueX}/{d.BlueY}");
50-
if (!string.IsNullOrWhiteSpace(d.WhiteX) && !string.IsNullOrWhiteSpace(d.WhiteY)) lines.Add($"White color coordinates X/Y: {d.WhiteX}/{d.WhiteY}");
51-
if (!string.IsNullOrWhiteSpace(d.LumaMin)) lines.Add($"Minimum luminance: { d.LumaMin}");
52-
if (!string.IsNullOrWhiteSpace(d.LumaMax)) lines.Add($"Maximum luminance: { d.LumaMax}");
53-
54-
if (lines.Count > 0)
55-
Logger.Log(string.Join("\n", lines));
56-
else
57-
Logger.Log($"No metadata found.");
42+
Logger.Log(d.ToString());
5843
}
5944
else
6045
{

ff-utils-winforms/Utils/ColorDataUtils.cs

Lines changed: 186 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -21,63 +21,72 @@ public static async Task<VideoColorData> GetColorData(string path)
2121

2222
string infoFfprobe = AvProcess.GetFfprobeOutput($"-v quiet -show_frames -read_intervals \"%+#1\" {path.Wrap()}");
2323

24-
if (infoFfprobe.Contains("side_data_type=Mastering display metadata"))
24+
string[] linesFfprobe = infoFfprobe.SplitIntoLines();
25+
26+
foreach (string line in linesFfprobe)
2527
{
26-
string[] linesFfprobe = infoFfprobe.Split("side_data_type=Mastering display metadata")[1].Split("[/SIDE_DATA]")[0].SplitIntoLines();
28+
if (line.Contains("color_transfer="))
29+
data.ColorTransfer = GetColorTransfer(line.Split('=').Last());
2730

28-
foreach (string line in linesFfprobe)
29-
{
30-
if (line.Contains("red_x="))
31-
data.RedX = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
31+
else if (line.Contains("color_space="))
32+
data.ColorMatrixCoeffs = GetMatrixCoeffs(line.Split('=').Last());
3233

33-
else if (line.Contains("red_y="))
34-
data.RedY = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
34+
else if (line.Contains("color_primaries="))
35+
data.ColorPrimaries = GetColorPrimaries(line.Split('=').Last());
3536

36-
else if (line.Contains("green_x="))
37-
data.GreenX = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
37+
else if (line.Contains("color_range="))
38+
data.ColorRange = GetColorRange(line.Split('=').Last());
3839

39-
else if (line.Contains("green_y="))
40-
data.GreenY = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
40+
else if (line.Contains("red_x="))
41+
data.RedX = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
4142

42-
else if (line.Contains("blue_x="))
43-
data.BlueY = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
43+
else if (line.Contains("red_y="))
44+
data.RedY = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
4445

45-
else if (line.Contains("blue_y="))
46-
data.BlueX = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
46+
else if (line.Contains("green_x="))
47+
data.GreenX = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
4748

48-
else if (line.Contains("white_point_x="))
49-
data.WhiteY = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
49+
else if (line.Contains("green_y="))
50+
data.GreenY = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
5051

51-
else if (line.Contains("white_point_y="))
52-
data.WhiteX = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
52+
else if (line.Contains("blue_x="))
53+
data.BlueY = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
5354

54-
else if (line.Contains("max_luminance="))
55-
data.LumaMax = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
55+
else if (line.Contains("blue_y="))
56+
data.BlueX = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
5657

57-
else if (line.Contains("min_luminance="))
58-
data.LumaMin = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
59-
}
58+
else if (line.Contains("white_point_x="))
59+
data.WhiteY = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
60+
61+
else if (line.Contains("white_point_y="))
62+
data.WhiteX = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
63+
64+
else if (line.Contains("max_luminance="))
65+
data.LumaMax = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
66+
67+
else if (line.Contains("min_luminance="))
68+
data.LumaMin = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
6069
}
6170

6271
string infoMkvinfo = await AvProcess.RunMkvInfo($"{path.Wrap()}");
63-
72+
6473
if (infoMkvinfo.Contains("+ Video track"))
6574
{
6675
string[] lines = infoMkvinfo.Split("+ Video track")[1].Split("+ Track")[0].Split("+ Tags")[0].SplitIntoLines();
6776

6877
foreach (string line in lines)
6978
{
7079
if (line.Contains("+ Colour transfer:"))
71-
data.ColorTransfer = ValidateNumber(line.Split(':')[1]);
80+
data.ColorTransfer = ValidateNumber(line.Split(':')[1]).GetInt();
7281

7382
else if (line.Contains("+ Colour matrix coefficients:"))
74-
data.ColorMatrixCoeffs = ValidateNumber(line.Split(':')[1]);
83+
data.ColorMatrixCoeffs = ValidateNumber(line.Split(':')[1]).GetInt();
7584

7685
else if (line.Contains("+ Colour primaries:"))
77-
data.ColorPrimaries = ValidateNumber(line.Split(':')[1]);
86+
data.ColorPrimaries = ValidateNumber(line.Split(':')[1]).GetInt();
7887

7988
else if (line.Contains("+ Colour range:"))
80-
data.ColorRange = ValidateNumber(line.Split(':')[1]);
89+
data.ColorRange = ValidateNumber(line.Split(':')[1]).GetInt();
8190

8291
else if (line.Contains("+ Red colour coordinate x:"))
8392
data.RedX = ValidateNumber(line.Split(':')[1]);
@@ -136,7 +145,7 @@ public static async Task SetColorData(string path, VideoColorData d)
136145
$"--colour-transfer-characteristics 0:{d.ColorTransfer} " +
137146
$"--colour-primaries 0:{d.ColorPrimaries} " +
138147
$"--max-luminance 0:{d.LumaMax} " +
139-
$"--min-luminance 0:{d.LumaMin} " +
148+
$"--min-luminance 0:{d.LumaMin} " +
140149
$"--chromaticity-coordinates 0:{d.RedX},{d.RedY},{d.GreenX},{d.GreenY},{d.BlueX},{d.BlueY} " +
141150
$"--white-colour-coordinates 0:{d.WhiteX},{d.WhiteY} " +
142151
$"{path.Wrap()}";
@@ -157,10 +166,155 @@ public static async Task SetColorData(string path, VideoColorData d)
157166
File.Move(tmpPath, path);
158167
}
159168
}
160-
catch(Exception e)
169+
catch (Exception e)
161170
{
162171
Logger.Log($"SetColorData Error: {e.Message}\n{e.StackTrace}");
163172
}
164173
}
174+
175+
public static int GetColorPrimaries(string s) // Defined by the "Color primaries" section of ISO/IEC 23091-4/ITU-T H.273
176+
{
177+
s = s.Trim().ToLower();
178+
if (s == "bt709") return 1;
179+
if (s == "bt470m") return 4;
180+
if (s == "bt470bg") return 5;
181+
if (s == "bt610") return 6;
182+
if (s == "smpte240m") return 7;
183+
if (s == "film") return 8;
184+
if (s == "bt2020") return 9;
185+
if (s == "smpte428") return 10;
186+
if (s == "smpte431") return 11;
187+
if (s == "smpte432") return 12;
188+
return 2; // Fallback: 2 = Unspecified
189+
}
190+
191+
public static int GetColorTransfer(string s) // Defined by the "Transfer characteristics" section of ISO/IEC 23091-4/ITU-T H.273
192+
{
193+
s = s.Trim().ToLower();
194+
if (s == "bt709") return 1;
195+
if (s == "gamma22" || s == "bt470m") return 4;
196+
if (s == "gamma28" || s == "bt470bg") return 5; // BT.470 System B, G (historical)
197+
if (s == "bt610" || s == "smpte170m") return 6; // BT.601
198+
if (s == "smpte240m") return 7; // SMPTE 240 M
199+
if (s == "linear") return 8; // Linear
200+
//if (s == "?") return 9; // Logarithmic(100 : 1 range)
201+
//if (s == "?") return 10; // Logarithmic (100 * Sqrt(10) : 1 range)
202+
if (s == "iec61966-2-4") return 11; // IEC 61966-2-4
203+
if (s == "bt1361" || s == "bt1361e") return 12; // BT.1361
204+
if (s == "srgb") return 13; // SRGB
205+
if (s == "bt2020-10") return 14; // BT.2020 10-bit systems
206+
if (s == "bt2020-12") return 15; // BT.2020 12-bit systems
207+
if (s == "smpte2084") return 16; // SMPTE ST 2084, ITU BT.2100 PQ
208+
if (s == "smpte428") return 17; // SMPTE ST 428
209+
if (s == "bt2100") return 18; // BT.2100 HLG, ARIB STD-B67
210+
return 2; // Fallback: 2 = Unspecified
211+
}
212+
213+
public static int GetMatrixCoeffs(string s) // Defined by the "Matrix coefficients" section of ISO/IEC 23091-4/ITU-T H.27
214+
{
215+
s = s.Trim().ToLower();
216+
if (s == "bt709") return 1;
217+
if (s == "fcc") return 4; // US FCC 73.628
218+
if (s == "bt470bg") return 5; // BT.470 System B, G (historical)
219+
if (s == "bt610" || s == "smpte170m") return 6; // BT.601
220+
if (s == "smpte240m") return 7; // SMPTE 240 M
221+
if (s == "ycgco") return 8; // YCgCo
222+
if (s == "bt2020ncl" || s == "bt2020nc") return 9; // BT.2020 non-constant luminance, BT.2100 YCbCr
223+
if (s == "bt2020") return 10; // BT.2020 constant luminance
224+
if (s == "smpte2085") return 11; // SMPTE ST 2085 YDzDx
225+
// 12: MC_CHROMAT_NCL - Chromaticity-derived non-constant luminance
226+
// 13: MC_CHROMAT_CL - Chromaticity-derived constant luminance
227+
// 14: MC_ICTCP BT.2100 - ICtCp
228+
return 2; // Fallback: 2 = Unspecified
229+
}
230+
231+
public static int GetColorRange(string s) // Defined by the "Matrix coefficients" section of ISO/IEC 23091-4/ITU-T H.27
232+
{
233+
s = s.Trim().ToLower();
234+
if (s == "tv") return 1; // TV
235+
if (s == "pc") return 2; // PC/Full
236+
return 0; // Fallback: Unspecified
237+
}
238+
239+
public static string GetColorPrimariesName(int n)
240+
{
241+
switch (n)
242+
{
243+
case 1: return "BT.709";
244+
case 2: return "Unspecified";
245+
case 4: return "BT.470 System B, G (historical)";
246+
case 5: return "BT.470 System M (historical)";
247+
case 6: return "BT.601";
248+
case 7: return "SMPTE 240";
249+
case 8: return "Generic film (color filters using illuminant C)";
250+
case 9: return "BT.2020, BT.2100";
251+
case 10: return "SMPTE 428 (CIE 1921 XYZ)";
252+
case 11: return "SMPTE RP 431-2";
253+
case 12: return "SMPTE EG 432-1";
254+
case 22: return "EBU Tech. 3213-E";
255+
}
256+
257+
return "Unknown";
258+
}
259+
260+
public static string GetColorTransferName(int n)
261+
{
262+
switch (n)
263+
{
264+
case 1: return "BT.709";
265+
case 2: return "Unspecified";
266+
case 4: return "BT.470 System B, G (historical)";
267+
case 5: return "BT.470 System M (historical)";
268+
case 6: return "BT.601";
269+
case 7: return "SMPTE 240 M";
270+
case 8: return "Linear";
271+
case 9: return "Logarithmic (100 : 1 range)";
272+
case 10: return "Logarithmic (100 * Sqrt(10) : 1 range)";
273+
case 11: return "IEC 61966-2-4";
274+
case 12: return "BT.1361";
275+
case 13: return "sRGB or sYCC";
276+
case 14: return "BT.2020 10-bit systems";
277+
case 15: return "BT.2020 12-bit systems";
278+
case 16: return "SMPTE ST 2084, ITU BT.2100 PQ";
279+
case 17: return "SMPTE ST 428";
280+
case 18: return "BT.2100 HLG, ARIB STD-B67";
281+
}
282+
283+
return "Unknown";
284+
}
285+
286+
public static string GetColorMatrixCoeffsName(int n)
287+
{
288+
switch (n)
289+
{
290+
case 1: return "BT.709";
291+
case 2: return "Unspecified";
292+
case 4: return "US FCC 73.628";
293+
case 5: return "BT.470 System B, G (historical)";
294+
case 6: return "BT.601";
295+
case 7: return "SMPTE 240 M";
296+
case 8: return "YCgCo";
297+
case 9: return "BT.2020 non-constant luminance, BT.2100 YCbCr";
298+
case 10: return "BT.2020 constant luminance";
299+
case 11: return "SMPTE ST 2085 YDzDx";
300+
case 12: return "Chromaticity-derived non-constant luminance";
301+
case 13: return "Chromaticity-derived constant luminance";
302+
case 14: return "BT.2100 ICtCp";
303+
}
304+
305+
return "Unknown";
306+
}
307+
308+
public static string GetColorRangeName(int n)
309+
{
310+
switch (n)
311+
{
312+
case 0: return "Unspecified";
313+
case 1: return "TV (Limited)";
314+
case 2: return "PC (Full)";
315+
}
316+
317+
return "Unknown";
318+
}
165319
}
166320
}

0 commit comments

Comments
 (0)