also looking at this
feat(video): add native AVC/HEVC and synchronized AAC MP4 streaming
#913
14 files
+1119
-0
| @@ -0,0 +1,37 @@ | ||
| 1 | //! Home RGB -> limited-range BT.601 I420. Adapted from perf/simd.zig, with | |
| 2 | //! checked strides and actual 2x2 chroma averaging rather than corner sampling. | |
| 3 | const std = ; | |
| 4 | pub fn convert(rgb: []const u8, width: u32, height: u32, stride: u32, out: []u8) !void { | |
| 5 | if (width == 0 or height == 0 or (width | height) & 1 != 0 or stride < @as(u64, width) * 3) return error.InvalidGeometry; | |
| 6 | const pixels = std.math.mul(usize, width, height) catch return error.InvalidGeometry; | |
| 7 | if (rgb.len < @as(u64, stride) * (height - 1) + @as(u64, width) * 3 or out.len < pixels + pixels / 2) return error.InvalidGeometry; | |
| 8 | for (0..height / 2) |cy| { | |
| 9 | for (0..width / 2) |cx| { | |
| 10 | var r: i32 = 0; | |
| 11 | var g: i32 = 0; | |
| 12 | var b: i32 = 0; | |
| 13 | for (0..2) |dy| { | |
| 14 | for (0..2) |dx| { | |
| 15 | const x = cx * 2 + dx; | |
| 16 | const y = cy * 2 + dy; | |
| 17 | const p = rgb[y * stride + x * 3 ..][0..3]; | |
| 18 | out[y * width + x] = @intCast(((66 * @as(i32, p[0]) + 129 * @as(i32, p[1]) + 25 * @as(i32, p[2]) + 128) >> 8) + 16); | |
| 19 | r += p[0]; | |
| 20 | g += p[1]; | |
| 21 | b += p[2]; | |
| 22 | } | |
| 23 | } | |
| 24 | const uv = cy * (width / 2) + cx; | |
| 25 | out[pixels + uv] = @intCast(((-38 * r - 74 * g + 112 * b + 512) >> 10) + 128); | |
| 26 | out[pixels + pixels / 4 + uv] = @intCast(((112 * r - 94 * g - 18 * b + 512) >> 10) + 128); | |
| 27 | } | |
| 28 | } | |
| 29 | } | |
| 30 | test "strided RGB conversion averages chroma and rejects truncation" { | |
| 31 | var out: [6]u8 = undefined; | |
| 32 | const rgb = [_]u8{ 255, 0, 0, 0, 255, 0, 99, 99, 0, 0, 255, 255, 255, 255 }; | |
| 33 | try convert(&rgb, 2, 2, 8, &out); | |
| 34 | try std.testing.expectEqualSlices(u8, &.{ 82, 144, 41, 235, 128, 128 }, &out); | |
| 35 | try std.testing.expectError(error.InvalidGeometry, convert(rgb[0..13], 2, 2, 8, &out)); | |
| 36 | try std.testing.expectError(error.InvalidGeometry, convert(&rgb, 1, 2, 8, &out)); | |
| 37 | } | |