nextav/docs/archive/transcoding-legacy/ANTI-JITTER-IMPLEMENTATION.md

359 lines
11 KiB
Markdown

# Anti-Jitter Progress System Implementation
## Overview
This document describes the implementation of Stash's anti-jitter mechanisms in NextAV to solve the streaming buffer jitter problem where progress bars jump backward as new data arrives.
## Problem Solved
**Before**: The progress bar would jump backward when:
- Buffer underruns occurred
- Network delays caused time to "catch up"
- Raw video time updates were directly reflected in the UI
**After**: Smooth, consistent progress that:
- Prevents backward jumps beyond a threshold
- Throttles updates to prevent excessive re-renders
- Allows small backward adjustments for smooth playback
- Provides visual buffer state feedback
## Implementation Details
### 1. Custom Hook: `useAntiJitterProgress`
**Location**: `src/lib/use-anti-jitter-progress.ts`
**Key Features**:
- **Jitter Threshold**: 0.5 seconds - maximum allowed backward jump
- **Update Throttling**: 100ms minimum between updates
- **Progress Reference**: Maintains stable progress state
- **Buffer Monitoring**: Tracks actual buffer state
**Core Logic**:
```typescript
const handleTimeUpdate = () => {
if (videoRef.current) {
const now = Date.now();
const video = videoRef.current;
const rawTime = video.currentTime;
const currentProgress = progressRef.current;
// Prevent backward jumps beyond threshold
if (rawTime < currentProgress - jitterThreshold) {
console.log(`[ANTI-JITTER] Blocked backward jump: ${rawTime}s -> ${currentProgress}s`);
return; // Block this update
}
// Throttle updates to prevent excessive re-renders
if (now - lastUpdateRef.current < updateThrottle) {
return;
}
// Validate and update progress
if (rawTime >= 0 && rawTime <= (duration || Infinity)) {
if (rawTime >= currentProgress - 0.1) {
// Forward progress or small adjustment
progressRef.current = rawTime;
setCurrentTime(rawTime);
lastUpdateRef.current = now;
}
}
}
};
```
### 2. Enhanced Progress Bar
**Features**:
- **Buffer Visualization**: Blue overlay shows buffered content
- **Smooth Progress**: Blue bar shows current playback position
- **Seek Overlay**: Invisible range input for seeking
- **Buffer Status**: Text display of buffered duration
**Visual Elements**:
```tsx
<div className="relative w-full h-2 bg-gray-600 rounded-lg overflow-hidden">
{/* Buffer indicator */}
{bufferState.buffered > 0 && (
<div
className="absolute top-0 h-full bg-blue-400/30 rounded-lg"
style={{
width: `${Math.min((bufferState.buffered / (duration || 1)) * 100, 100)}%`,
left: '0'
}}
/>
)}
{/* Progress indicator */}
<div
className="absolute top-0 h-full bg-blue-500 rounded-lg transition-all duration-100"
style={{
width: `${Math.min((currentTime / (duration || 1)) * 100, 100)}%`,
left: '0'
}}
/>
{/* Seek input overlay */}
<input
type="range"
min="0"
max={duration || 0}
value={currentTime}
onChange={handleSeek}
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
/>
</div>
```
### 3. Anti-Jitter Mechanisms
#### A. Backward Jump Prevention
- **Threshold**: 0.5 seconds maximum backward jump
- **Logic**: Blocks updates that would cause large backward movement
- **Benefit**: Prevents jarring progress bar jumps
#### B. Update Throttling
- **Frequency**: Maximum 10 updates per second (100ms throttle)
- **Logic**: Skips updates that come too quickly
- **Benefit**: Smoother UI performance, less CPU usage
#### C. Small Adjustment Allowance
- **Threshold**: 0.1 seconds for small backward adjustments
- **Logic**: Allows minor corrections for smooth playback
- **Benefit**: Maintains playback quality while preventing large jumps
#### D. Progress Reference Management
- **State**: Maintains stable progress reference
- **Updates**: Only updates when progress is valid
- **Reset**: Resets on video source changes
### 4. Buffer State Monitoring
**Features**:
- **Real-time Tracking**: Monitors `progress` events
- **Visual Feedback**: Shows buffered content in progress bar
- **Status Display**: Shows buffered duration below progress bar
**Implementation**:
```typescript
const handleProgress = () => {
if (videoRef.current) {
const video = videoRef.current;
if (video.buffered.length > 0) {
const bufferedEnd = video.buffered.end(video.buffered.length - 1);
bufferStateRef.current = {
buffered: bufferedEnd,
lastBufferUpdate: Date.now()
};
console.log(`[BUFFER] Buffered to ${bufferedEnd}s`);
}
}
};
```
## Usage
### 1. In Video Components
```typescript
import { useAntiJitterProgress } from '@/lib/use-anti-jitter-progress';
export default function VideoViewer({ video, isOpen, onClose }) {
const videoRef = useRef<HTMLVideoElement>(null);
// Use the anti-jitter progress hook
const {
currentTime,
bufferState,
handleTimeUpdate,
handleProgress,
seekTo,
resetProgress
} = useAntiJitterProgress(videoRef, duration);
// Add event listeners
useEffect(() => {
if (videoRef.current) {
videoRef.current.addEventListener('timeupdate', handleTimeUpdate);
videoRef.current.addEventListener('progress', handleProgress);
return () => {
videoRef.current?.removeEventListener('timeupdate', handleTimeUpdate);
videoRef.current?.removeEventListener('progress', handleProgress);
};
}
}, []);
// Handle seeking
const handleSeek = (e: React.ChangeEvent<HTMLInputElement>) => {
const newTime = parseFloat(e.target.value);
seekTo(newTime);
};
// Reset on video change
useEffect(() => {
resetProgress();
}, [video]);
}
```
### 2. Enhanced Progress Bar
```tsx
{/* Enhanced Progress bar with buffer visualization */}
<div className="mb-4">
<div className="relative w-full h-2 bg-gray-600 rounded-lg overflow-hidden">
{/* Buffer indicator */}
{bufferState.buffered > 0 && (
<div
className="absolute top-0 h-full bg-blue-400/30 rounded-lg"
style={{
width: `${Math.min((bufferState.buffered / (duration || 1)) * 100, 100)}%`,
left: '0'
}}
/>
)}
{/* Progress indicator */}
<div
className="absolute top-0 h-full bg-blue-500 rounded-lg transition-all duration-100"
style={{
width: `${Math.min((currentTime / (duration || 1)) * 100, 100)}%`,
left: '0'
}}
/>
{/* Seek input overlay */}
<input
type="range"
min="0"
max={duration || 0}
value={currentTime}
onChange={handleSeek}
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
/>
</div>
<div className="flex justify-between text-white text-sm mt-1">
<span>{formatTime(currentTime)}</span>
<span>{formatTime(duration)}</span>
</div>
{/* Buffer status */}
{bufferState.buffered > 0 && (
<div className="text-xs text-blue-300 mt-1">
Buffered: {formatTime(bufferState.buffered)}
</div>
)}
</div>
```
## Configuration
### Jitter Threshold
```typescript
const jitterThreshold = 0.5; // Maximum allowed backward jump in seconds
```
- **Lower values**: More strict, less jitter but potentially choppy
- **Higher values**: More lenient, smoother but potential for jumps
- **Recommended**: 0.5 seconds (good balance)
### Update Throttle
```typescript
const updateThrottle = 100; // Minimum ms between updates
```
- **Lower values**: More responsive but higher CPU usage
- **Higher values**: Smoother but less responsive
- **Recommended**: 100ms (10 FPS, good balance)
### Small Adjustment Threshold
```typescript
if (rawTime >= currentProgress - 0.1) // 0.1 seconds
```
- **Lower values**: Stricter progress validation
- **Higher values**: More lenient with small adjustments
- **Recommended**: 0.1 seconds (allows smooth playback)
## Testing
### Test Script
Run the test script to verify the anti-jitter system:
```bash
node test-anti-jitter.mjs
```
### Test Scenarios
1. **Normal Forward Progress**: All forward updates should pass
2. **Small Backward Adjustment**: Small adjustments should pass
3. **Large Backward Jump**: Large backward jumps should be blocked
4. **Rapid Updates**: Rapid updates should be throttled
## Benefits
### 1. User Experience
- **Smooth Progress**: No more jarring backward jumps
- **Visual Feedback**: Clear buffer state indication
- **Consistent Behavior**: Predictable progress bar movement
### 2. Performance
- **Reduced Re-renders**: Throttled updates improve performance
- **Stable State**: Progress reference prevents unnecessary updates
- **Efficient Monitoring**: Smart event handling
### 3. Reliability
- **Jitter Prevention**: Blocks problematic time updates
- **Buffer Awareness**: Tracks actual buffered content
- **Error Handling**: Graceful fallbacks for edge cases
## Future Enhancements
### 1. Adaptive Thresholds
- **Dynamic Jitter Threshold**: Adjust based on video quality
- **Network-Aware Throttling**: Adapt to connection speed
- **Quality-Based Settings**: Different thresholds for different scenarios
### 2. Advanced Buffer Management
- **Predictive Buffering**: Anticipate buffer needs
- **Quality Adaptation**: Switch quality based on buffer state
- **Network Monitoring**: Track connection health
### 3. Enhanced Visualization
- **Buffer Prediction**: Show predicted buffer state
- **Quality Indicators**: Visual quality level indicators
- **Network Status**: Connection health indicators
## Troubleshooting
### Common Issues
#### 1. Progress Bar Not Moving
- Check if `handleTimeUpdate` is being called
- Verify `jitterThreshold` isn't too restrictive
- Ensure video element has valid duration
#### 2. Excessive Throttling
- Increase `updateThrottle` value
- Check for rapid timeupdate events
- Verify video source stability
#### 3. Buffer Not Showing
- Ensure `handleProgress` is attached to `progress` event
- Check if video has buffered ranges
- Verify buffer state updates
### Debug Logging
The system provides comprehensive logging:
```
[ANTI-JITTER] Blocked backward jump: 2s -> 5s
[THROTTLE] Skipped update: 50ms < 100ms
[BUFFER] Buffered to 10s
[SEEK] Seeking to 15s, updated progress reference
[PROGRESS] Forward progress: 16s
```
## Conclusion
The anti-jitter progress system successfully implements Stash's approach to solve streaming buffer jitter. By preventing backward jumps, throttling updates, and providing visual feedback, it creates a smooth, professional video playback experience.
The system is:
- **Configurable**: Easy to adjust thresholds and behavior
- **Reusable**: Shared hook for multiple components
- **Efficient**: Minimal performance impact
- **Reliable**: Handles edge cases gracefully
This implementation provides the foundation for professional-grade video streaming without the jittery behavior common in basic implementations.