Skip to content

Commit 61b3df2

Browse files
pblazejclaude
andcommitted
test: feed frames continuously when publishing buffer tracks
`publishManyTracks` captured a single frame before each `publish(videoTrack:)` and discarded `CVPixelBufferCreate`'s result, so a failed allocation captured nothing at all. `BufferCapturer` documents both halves of what that breaks: `capture(_:)` is meant to be called repeatedly, and dimensions must resolve before publishing or the publish times out — they come only from a real frame, not from `BufferCaptureOptions(dimensions:)`. Server logs show the effect: five of six tracks published in 2-6ms each, then video-2 sat for 10.6s and failed with `Timed out` without any `add_track` reaching the SFU. Keep frames flowing until publish returns, matching how `PublishBufferCapturerTests` drives its buffer track. A fresh buffer per capture keeps anything non-Sendable out of the feeding task, and `#require` replaces the discarded allocation result. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent d443dd8 commit 61b3df2

1 file changed

Lines changed: 22 additions & 6 deletions

File tree

Tests/LiveKitCoreTests/PeerConnectionSignalingTests.swift

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -348,13 +348,19 @@ struct PeerConnectionSignalingTests {
348348

349349
for i in 0 ..< videoCount {
350350
let track = LocalVideoTrack.createBufferTrack(name: "video-\(i)")
351-
guard let capturer = track.capturer as? BufferCapturer else {
352-
Issue.record("Expected BufferCapturer")
353-
return
351+
let capturer = try #require(track.capturer as? BufferCapturer)
352+
353+
// `BufferCapturer` resolves dimensions only from captured frames and
354+
// is documented to be fed repeatedly, so keep frames flowing until
355+
// publish returns rather than relying on a single one landing in time.
356+
let frames = Task.detached {
357+
while !Task.isCancelled {
358+
if let buffer = Self.makeTestPixelBuffer() { capturer.capture(buffer) }
359+
try? await Task.sleep(nanoseconds: 50_000_000)
360+
}
354361
}
355-
var pixelBuffer: CVPixelBuffer?
356-
CVPixelBufferCreate(kCFAllocatorDefault, 320, 240, kCVPixelFormatType_32BGRA, nil, &pixelBuffer)
357-
if let pixelBuffer { capturer.capture(pixelBuffer) }
362+
defer { frames.cancel() }
363+
358364
try await room1.localParticipant.publish(videoTrack: track)
359365
try await Task.sleep(nanoseconds: 500_000_000)
360366
}
@@ -392,6 +398,16 @@ struct PeerConnectionSignalingTests {
392398
}
393399
}
394400

401+
/// A fresh blank frame. Created per capture so nothing non-Sendable crosses
402+
/// into the feeding task.
403+
private static func makeTestPixelBuffer() -> CVPixelBuffer? {
404+
var pixelBuffer: CVPixelBuffer?
405+
guard CVPixelBufferCreate(kCFAllocatorDefault, 320, 240, kCVPixelFormatType_32BGRA, nil, &pixelBuffer) == kCVReturnSuccess else {
406+
return nil
407+
}
408+
return pixelBuffer
409+
}
410+
395411
private static var isLocalhost: Bool {
396412
let url = TestEnvironment.liveKitServerUrl()
397413
return url.contains("localhost") || url.contains("127.0.0.1")

0 commit comments

Comments
 (0)