@@ -323,6 +323,7 @@ export class RFWebRTCConnection {
323323 */
324324 public readonly dataChannel : RTCDataChannel ;
325325 private reassembler : ChunkReassembler ;
326+ private ackPacingEnabled : boolean ;
326327 /**
327328 * The data channel used for uploading video files (only available in file upload mode).
328329 * Exposed for advanced use cases.
@@ -343,6 +344,8 @@ export class RFWebRTCConnection {
343344 uploadChannel ?: RTCDataChannel ;
344345 onData ?: ( data : any ) => void ;
345346 onComplete ?: ( ) => void ;
347+ /** @internal Enable server pacing via cumulative ACKs (only used when realtimeProcessing=false). */
348+ ackPacingEnabled ?: boolean ;
346349 }
347350 ) {
348351 this . peerConnection = pc ;
@@ -352,6 +355,7 @@ export class RFWebRTCConnection {
352355 this . apiKey = apiKey ;
353356 this . dataChannel = dataChannel ;
354357 this . reassembler = new ChunkReassembler ( ) ;
358+ this . ackPacingEnabled = options ?. ackPacingEnabled === true ;
355359 this . uploadChannel = options ?. uploadChannel ;
356360 this . onComplete = options ?. onComplete ;
357361
@@ -374,7 +378,11 @@ export class RFWebRTCConnection {
374378 const decoder = new TextDecoder ( "utf-8" ) ;
375379 const jsonString = decoder . decode ( completePayload ) ;
376380 const data = JSON . parse ( jsonString ) ;
377- onData ( data ) ;
381+ // Wait for onData completion (supports async handlers) before ACKing the frame.
382+ Promise . resolve ( onData ( data ) )
383+ . finally ( ( ) => {
384+ this . maybeSendAck ( frameId ) ;
385+ } )
378386 }
379387 } else {
380388 // Fallback for string messages (shouldn't happen with new protocol)
@@ -400,6 +408,17 @@ export class RFWebRTCConnection {
400408 } ) ;
401409 }
402410
411+ /**
412+ * Send cumulative ACK after a frame is fully handled.
413+ * Only used in batch mode (realtimeProcessing=false).
414+ */
415+ private maybeSendAck ( frameId : number ) : void {
416+ if ( ! this . ackPacingEnabled ) return ;
417+ if ( this . dataChannel . readyState !== "open" ) return ;
418+
419+ this . dataChannel . send ( JSON . stringify ( { ack : frameId } ) ) ;
420+ }
421+
403422 /**
404423 * Get the remote stream (processed video from Roboflow)
405424 *
@@ -706,6 +725,7 @@ async function baseUseStream({
706725 const apiKey = connector . _apiKey || null ;
707726
708727 // Step 7: Create connection object
728+ const ackPacingEnabled = resolvedWrtcParams . realtimeProcessing === false ;
709729 const connection = new RFWebRTCConnection (
710730 pc ,
711731 remoteStreamPromise ,
@@ -716,7 +736,8 @@ async function baseUseStream({
716736 localStream,
717737 uploadChannel,
718738 onData,
719- onComplete
739+ onComplete,
740+ ackPacingEnabled
720741 }
721742 ) ;
722743
0 commit comments