mirror of
https://github.com/block/buzz.git
synced 2026-10-30 14:01:37 +00:00
## Summary - add iOS and Android voice-note recording and preview directly in the mobile composer - add waveform playback, scrubbing, speed controls, haptics, and one-shot playback in chat - package recordings in a canonical H.264/AAC MP4 envelope on both platforms so existing relays accept them - preserve the shared composer interaction and attachment-card treatment across mobile platforms Mobile counterpart to #6978. ## Testing - `just ci` - `just mobile-check` - `just mobile-test` (2,026 tests) - Android debug build compiled, installed, launched, and Voice note verified in the attachment menu on Pixel 10 - signed iOS device build installed on iPhone --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Signed-off-by: Kenny Lopez <klopez4212@gmail.com> Signed-off-by: Carl <3c4caeafb646d23867f1c4832e68211d77e2561946171625f75c3ce1a3f2670f@buzz.block.builderlab.xyz> Co-authored-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> Co-authored-by: Carl <3c4caeafb646d23867f1c4832e68211d77e2561946171625f75c3ce1a3f2670f@buzz.block.builderlab.xyz>
74 lines
2.4 KiB
Swift
74 lines
2.4 KiB
Swift
import Foundation
|
|
|
|
enum MP4Canonicalizer {
|
|
static func neutralizeSampleDependencyBoxes(at url: URL) throws {
|
|
var data = try Data(contentsOf: url)
|
|
try neutralizeSampleDependencyBoxes(in: &data, start: 0, end: data.count)
|
|
try data.write(to: url, options: .atomic)
|
|
}
|
|
|
|
private static func neutralizeSampleDependencyBoxes(
|
|
in data: inout Data,
|
|
start: Int,
|
|
end: Int
|
|
) throws {
|
|
let containers: Set<[UInt8]> = [
|
|
Array("moov".utf8), Array("trak".utf8), Array("mdia".utf8),
|
|
Array("minf".utf8), Array("stbl".utf8), Array("edts".utf8),
|
|
Array("dinf".utf8), Array("sinf".utf8), Array("schi".utf8),
|
|
]
|
|
let sampleDependencyType = Array("sdtp".utf8)
|
|
let freeType = Array("free".utf8)
|
|
var offset = start
|
|
|
|
while offset < end {
|
|
guard end - offset >= 8 else { throw invalidMp4BoxError() }
|
|
let compactSize = Int(readBigEndianUInt32(data, at: offset))
|
|
var headerSize = 8
|
|
let boxSize: Int
|
|
if compactSize == 1 {
|
|
guard end - offset >= 16 else { throw invalidMp4BoxError() }
|
|
let extendedSize = readBigEndianUInt64(data, at: offset + 8)
|
|
guard extendedSize <= UInt64(Int.max) else { throw invalidMp4BoxError() }
|
|
boxSize = Int(extendedSize)
|
|
headerSize = 16
|
|
} else if compactSize == 0 {
|
|
boxSize = end - offset
|
|
} else {
|
|
boxSize = compactSize
|
|
}
|
|
|
|
guard boxSize >= headerSize, offset + boxSize <= end else {
|
|
throw invalidMp4BoxError()
|
|
}
|
|
let type = Array(data[(offset + 4)..<(offset + 8)])
|
|
if type == sampleDependencyType {
|
|
data.replaceSubrange((offset + 4)..<(offset + 8), with: freeType)
|
|
} else if containers.contains(type) {
|
|
try neutralizeSampleDependencyBoxes(
|
|
in: &data,
|
|
start: offset + headerSize,
|
|
end: offset + boxSize
|
|
)
|
|
}
|
|
offset += boxSize
|
|
}
|
|
}
|
|
|
|
private static func readBigEndianUInt32(_ data: Data, at offset: Int) -> UInt32 {
|
|
data[offset..<(offset + 4)].reduce(0) { ($0 << 8) | UInt32($1) }
|
|
}
|
|
|
|
private static func readBigEndianUInt64(_ data: Data, at offset: Int) -> UInt64 {
|
|
data[offset..<(offset + 8)].reduce(0) { ($0 << 8) | UInt64($1) }
|
|
}
|
|
|
|
private static func invalidMp4BoxError() -> NSError {
|
|
NSError(
|
|
domain: "BuzzVideoTranscode",
|
|
code: 1,
|
|
userInfo: [NSLocalizedDescriptionKey: "Invalid MP4 box structure."]
|
|
)
|
|
}
|
|
}
|