263 lines
6.3 KiB
Markdown
263 lines
6.3 KiB
Markdown
# Phase 1 Implementation Summary - Playback Timing Fix
|
|
|
|
**Date**: 2025-10-07
|
|
**Status**: ✅ Implemented, Ready for Testing
|
|
|
|
## Changes Made
|
|
|
|
### 1. PlaybackController.h
|
|
**Added**:
|
|
- Forward declaration: `class FrameProcessor;`
|
|
- Public method: `void SetFrameProcessor(FrameProcessor* processor)`
|
|
- Public method: `bool IsFrameProcessing() const`
|
|
- Private member: `FrameProcessor* m_frameProcessor = nullptr`
|
|
|
|
**Lines changed**: +5 lines
|
|
|
|
### 2. PlaybackController.cpp
|
|
**Added**:
|
|
- Include: `#include "FrameProcessor.h"`
|
|
- Implementation of `IsFrameProcessing()` method
|
|
- Frame completion wait logic in `TimingThreadLoop()`
|
|
|
|
**Modified**:
|
|
```cpp
|
|
void PlaybackController::TimingThreadLoop()
|
|
{
|
|
// ... existing code ...
|
|
|
|
while (!m_shouldStopTiming && m_isPlaying) {
|
|
auto frameStart = std::chrono::high_resolution_clock::now();
|
|
|
|
// Signal frame processing
|
|
if (m_frameReadyCallback) {
|
|
m_frameReadyCallback();
|
|
}
|
|
|
|
// ✅ NEW: Wait for frame processing completion (max 100ms timeout)
|
|
int waitCount = 0;
|
|
while (IsFrameProcessing() && waitCount < 100) {
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
waitCount++;
|
|
}
|
|
|
|
if (waitCount >= 100) {
|
|
LOGF_WARNING("[PlaybackController] Frame processing timeout after 100ms");
|
|
}
|
|
|
|
// Update frame counter
|
|
m_currentFrame++;
|
|
m_currentTime = m_currentFrame / m_frameRate;
|
|
|
|
// Sleep until next frame
|
|
std::this_thread::sleep_until(nextFrame);
|
|
}
|
|
}
|
|
|
|
bool PlaybackController::IsFrameProcessing() const
|
|
{
|
|
return m_frameProcessor && m_frameProcessor->IsProcessing();
|
|
}
|
|
```
|
|
|
|
**Lines changed**: +20 lines
|
|
|
|
### 3. VideoPlayerControl2.xaml.cpp
|
|
**Modified**:
|
|
```cpp
|
|
void VideoPlayerControl2::LoadVideo(...)
|
|
{
|
|
// ... existing code ...
|
|
|
|
if (success) {
|
|
// ✅ NEW: Link FrameProcessor to PlaybackController
|
|
if (m_frameProcessor) {
|
|
m_playbackController->SetFrameProcessor(m_frameProcessor.get());
|
|
m_frameProcessor->PrepareVideoTexture(videoWidth, videoHeight);
|
|
}
|
|
|
|
// ... rest of code ...
|
|
}
|
|
}
|
|
```
|
|
|
|
**Lines changed**: +1 line
|
|
|
|
---
|
|
|
|
## Total Changes
|
|
- **Files modified**: 3
|
|
- **Lines added**: ~26 lines
|
|
- **Complexity**: Very Low
|
|
- **Risk**: Minimal
|
|
|
|
---
|
|
|
|
## How It Works
|
|
|
|
### Before Fix (Broken)
|
|
```
|
|
Timeline:
|
|
0ms : Timing thread signals "process frame N"
|
|
0ms : FrameProcessor starts decode
|
|
10-15ms : CUDA decode completes
|
|
20-25ms : UI render completes
|
|
33.33ms : Timing thread signals "process frame N+1" (DOESN'T WAIT!)
|
|
→ If frame N not done, m_frameProcessing check FAILS
|
|
→ Frame N+1 DROPPED
|
|
→ Result: Jerky playback
|
|
```
|
|
|
|
### After Fix (Working)
|
|
```
|
|
Timeline:
|
|
0ms : Timing thread signals "process frame N"
|
|
0ms : FrameProcessor starts decode (m_frameProcessing = true)
|
|
10-15ms : CUDA decode completes
|
|
20-25ms : UI render completes
|
|
25ms : FrameProcessor done (m_frameProcessing = false)
|
|
25ms : Timing thread detects completion via IsFrameProcessing()
|
|
25ms : Timing thread advances frame counter
|
|
33.33ms : Sleep completes, signal "process frame N+1"
|
|
→ NO FRAME DROPS
|
|
→ Result: Smooth playback
|
|
```
|
|
|
|
---
|
|
|
|
## Build Status
|
|
✅ **Build successful**
|
|
- VavCore-debug.dll: Built
|
|
- Vav2Player.exe: Built
|
|
- No errors, no warnings (related to changes)
|
|
|
|
---
|
|
|
|
## Testing Instructions
|
|
|
|
### 1. Launch Application
|
|
```bash
|
|
cd "D:/Project/video-av1/vav2/platforms/windows/applications/vav2player/Vav2Player/x64/Debug/Vav2Player"
|
|
./Vav2Player.exe
|
|
```
|
|
|
|
### 2. Load Test Video
|
|
- Use file picker to load: `D:/Project/video-av1/sample/simple_test.webm`
|
|
- Or any 30fps AV1/VP9 video
|
|
|
|
### 3. Observe Playback
|
|
**Expected Results**:
|
|
- ✅ Smooth playback (no jerking)
|
|
- ✅ Consistent 30fps
|
|
- ✅ Zero frame drops
|
|
|
|
**Check Logs**:
|
|
```
|
|
[PlaybackController] Frame processing timeout after 100ms ← Should NOT appear
|
|
[FrameProcessor] Frame dropped (#X) ← Should be 0
|
|
```
|
|
|
|
### 4. Long Duration Test
|
|
- Play for 5 minutes
|
|
- Monitor for any jerking
|
|
- Check frame drop counter: should remain 0
|
|
|
|
---
|
|
|
|
## Success Criteria
|
|
|
|
### Primary
|
|
- [ ] Video plays smoothly (no "통통 튐")
|
|
- [ ] No frame drops during 5-minute playback
|
|
- [ ] User confirms smooth visual playback
|
|
|
|
### Secondary
|
|
- [ ] Logs show no timeout warnings
|
|
- [ ] Frame intervals consistent at 33.33ms
|
|
- [ ] CPU usage same as before
|
|
|
|
---
|
|
|
|
## If Problems Occur
|
|
|
|
### Rollback Procedure
|
|
1. Revert PlaybackController.h:
|
|
- Remove forward declaration
|
|
- Remove SetFrameProcessor and IsFrameProcessing methods
|
|
- Remove m_frameProcessor member
|
|
|
|
2. Revert PlaybackController.cpp:
|
|
- Remove FrameProcessor.h include
|
|
- Restore original TimingThreadLoop
|
|
- Remove IsFrameProcessing implementation
|
|
|
|
3. Revert VideoPlayerControl2.xaml.cpp:
|
|
- Remove SetFrameProcessor call
|
|
|
|
**Time**: < 5 minutes
|
|
|
|
### Alternative: Git Revert
|
|
```bash
|
|
git diff HEAD # Review changes
|
|
git checkout -- <file> # Revert specific file
|
|
```
|
|
|
|
---
|
|
|
|
## Next Steps (Phase 2)
|
|
|
|
**If Phase 1 successful**:
|
|
- Proceed with Fence removal (optional cleanup)
|
|
- See: `Playback_Timing_Fix_Design.md` Phase 2
|
|
|
|
**If Phase 1 issues**:
|
|
- Debug specific problem
|
|
- Check FrameProcessor::IsProcessing() behavior
|
|
- Verify m_frameProcessing atomic flag operations
|
|
|
|
---
|
|
|
|
## Technical Notes
|
|
|
|
### Thread Safety
|
|
- `IsFrameProcessing()` reads atomic bool `m_frameProcessing` ✅ Thread-safe
|
|
- `SetFrameProcessor()` called during initialization only ✅ No race condition
|
|
- Non-owning pointer (FrameProcessor lifetime > PlaybackController) ✅ Safe
|
|
|
|
### Performance Impact
|
|
- Wait loop: 1ms sleep per iteration
|
|
- Typical wait: 20-25ms (20-25 iterations)
|
|
- CPU overhead: Negligible (< 1%)
|
|
|
|
### Timeout Behavior
|
|
- Max wait: 100ms (100 iterations)
|
|
- If timeout occurs: Warning logged, playback continues
|
|
- Prevents infinite loop if frame processor hangs
|
|
|
|
---
|
|
|
|
## Logs to Monitor
|
|
|
|
### Good Playback
|
|
```
|
|
[PlaybackController] Timing thread started
|
|
[FrameProcessor] ProcessFrame START (decoded: 0, dropped: 0)
|
|
[FrameProcessor] Decode SUCCESS - frame decoded
|
|
[FrameProcessor] Render succeeded
|
|
[FrameProcessor] CLEARING m_frameProcessing flag
|
|
[PlaybackController] [No timeout warnings]
|
|
```
|
|
|
|
### Bad Playback (Should NOT see)
|
|
```
|
|
[PlaybackController] Frame processing timeout after 100ms
|
|
[FrameProcessor] Frame dropped (#1) - previous frame still processing
|
|
```
|
|
|
|
---
|
|
|
|
## Contact
|
|
**Implementation**: Claude Code
|
|
**Review**: User
|
|
**Date**: 2025-10-07
|