commit 4beb6d77a3fae93785ee5b8482e486b8d258fcc9
Author: Mark Spieth <mspieth@digivation.com.au>
Date: Tue Apr 27 07:51:51 2010 +1000
smoother vsync with predictive frame skipping
diff --git a/mythtv/libs/libmythtv/NuppelVideoPlayer.cpp b/mythtv/libs/libmythtv/NuppelVideoPlayer.cpp
index ce2b45d..88a071b 100644
a
|
b
|
NuppelVideoPlayer::NuppelVideoPlayer(bool muted) |
205 | 205 | videosync(NULL), delay(0), |
206 | 206 | vsynctol(30/4), avsync_delay(0), |
207 | 207 | avsync_adjustment(0), avsync_avg(0), |
208 | | avsync_oldavg(0), refreshrate(0), |
| 208 | avsync_oldavg(0), |
| 209 | avsync_predictor(0), avsync_predictor_enabled(false), |
| 210 | refreshrate(0), |
209 | 211 | lastsync(false), m_playing_slower(false), |
210 | 212 | m_stored_audio_stretchfactor(1.0), |
211 | 213 | audio_paused(false), |
… |
… |
NuppelVideoPlayer::NuppelVideoPlayer(bool muted) |
237 | 239 | db_prefer708 = gContext->GetNumSetting("Prefer708Captions", 1); |
238 | 240 | autocommercialskip = (CommSkipMode) |
239 | 241 | gContext->GetNumSetting("AutoCommercialSkip", kCommSkipOff); |
| 242 | usesmoothsync = gContext->GetNumSetting("UseSmoothSync", 1) != 0; |
240 | 243 | |
241 | 244 | lastIgnoredManualSkip = QDateTime::currentDateTime().addSecs(-10); |
242 | 245 | |
… |
… |
void NuppelVideoPlayer::SetVideoParams(int width, int height, double fps, |
1119 | 1122 | video_frame_rate = fps; |
1120 | 1123 | float temp_speed = (play_speed == 0.0f) ? |
1121 | 1124 | audio_stretchfactor : play_speed; |
1122 | | frame_interval = (int)(1000000.0f / video_frame_rate / temp_speed); |
| 1125 | SetFrameInterval(kScan_Progressive, 1.0 / (video_frame_rate * temp_speed)); |
1123 | 1126 | } |
1124 | 1127 | |
1125 | 1128 | if (videoOutput) |
… |
… |
float NuppelVideoPlayer::WarpFactor(void) |
2311 | 2314 | return divergence; |
2312 | 2315 | } |
2313 | 2316 | |
| 2317 | void NuppelVideoPlayer::SetFrameInterval(FrameScanType scan, double frame_period) |
| 2318 | { |
| 2319 | frame_interval = (int)(1000000.0f * frame_period + 0.5f); |
| 2320 | avsync_predictor = 0; |
| 2321 | avsync_predictor_enabled = false; |
| 2322 | |
| 2323 | VERBOSE(VB_PLAYBACK, LOC + QString("SetFrameInterval ps:%1 scan:%2 usesmoothsync:%3") |
| 2324 | .arg(play_speed).arg(scan).arg(usesmoothsync) |
| 2325 | ); |
| 2326 | //if (play_speed <= 1 || play_speed > 2 || scan != kScan_Progressive || !usesmoothsync) |
| 2327 | if (play_speed < 1 || play_speed > 2 || refreshrate <= 0 || !usesmoothsync) |
| 2328 | return; |
| 2329 | |
| 2330 | avsync_predictor_enabled = ((frame_interval-(frame_interval/200)) < refreshrate); |
| 2331 | } |
| 2332 | |
2314 | 2333 | void NuppelVideoPlayer::InitAVSync(void) |
2315 | 2334 | { |
2316 | 2335 | videosync->Start(); |
… |
… |
void NuppelVideoPlayer::InitAVSync(void) |
2334 | 2353 | .arg(refreshrate).arg(frame_interval); |
2335 | 2354 | VERBOSE(VB_PLAYBACK, msg); |
2336 | 2355 | |
| 2356 | SetFrameInterval(m_scan, 1.0 / (video_frame_rate * play_speed)); |
| 2357 | |
2337 | 2358 | // try to get preferential scheduling, but ignore if we fail to. |
2338 | 2359 | myth_nice(-19); |
2339 | 2360 | } |
… |
… |
void NuppelVideoPlayer::AVSync(void) |
2381 | 2402 | ps = kScan_Progressive; |
2382 | 2403 | |
2383 | 2404 | bool dropframe = false; |
| 2405 | QString dbg; |
| 2406 | |
| 2407 | if (avsync_predictor_enabled) |
| 2408 | { |
| 2409 | avsync_predictor += frame_interval; |
| 2410 | if (avsync_predictor >= refreshrate) |
| 2411 | { |
| 2412 | int refreshperiodsinframe = avsync_predictor/refreshrate; |
| 2413 | avsync_predictor -= refreshrate * refreshperiodsinframe; |
| 2414 | } |
| 2415 | else |
| 2416 | { |
| 2417 | dropframe = true; |
| 2418 | dbg = "A/V predict drop frame, "; |
| 2419 | } |
| 2420 | } |
| 2421 | |
2384 | 2422 | if (diverge < -MAXDIVERGE) |
2385 | 2423 | { |
2386 | 2424 | dropframe = true; |
2387 | 2425 | // If video is way behind of audio, adjust for it... |
2388 | | QString dbg = QString("Video is %1 frames behind audio (too slow), ") |
| 2426 | dbg = QString("Video is %1 frames behind audio (too slow), ") |
2389 | 2427 | .arg(-diverge); |
| 2428 | } |
2390 | 2429 | |
| 2430 | if (dropframe) |
| 2431 | { |
2391 | 2432 | // Reset A/V Sync |
2392 | 2433 | lastsync = true; |
2393 | 2434 | |
… |
… |
void NuppelVideoPlayer::AVSync(void) |
2517 | 2558 | #if 1 |
2518 | 2559 | VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, QString( |
2519 | 2560 | "A/V timecodes audio %1 video %2 frameinterval %3 " |
2520 | | "avdel %4 avg %5 tcoffset %6") |
| 2561 | "avdel %4 avg %5 tcoffset %6" |
| 2562 | " avp %7 avpen %8" |
| 2563 | ) |
2521 | 2564 | .arg(currentaudiotime) |
2522 | 2565 | .arg(buffer->timecode) |
2523 | 2566 | .arg(frame_interval) |
2524 | 2567 | .arg(buffer->timecode - currentaudiotime) |
2525 | 2568 | .arg(avsync_avg) |
2526 | 2569 | .arg(tc_wrap[TC_AUDIO]) |
| 2570 | .arg(avsync_predictor) |
| 2571 | .arg(avsync_predictor_enabled) |
2527 | 2572 | ); |
2528 | 2573 | #endif |
2529 | 2574 | if (currentaudiotime != 0 && buffer->timecode != 0) |
… |
… |
void NuppelVideoPlayer::AVSync(void) |
2532 | 2577 | // last->timecode |
2533 | 2578 | int delta = (int)((buffer->timecode - prevtc)/play_speed) - (frame_interval / 1000); |
2534 | 2579 | prevtc = buffer->timecode; |
2535 | | //cerr << delta << " "; |
2536 | 2580 | |
2537 | 2581 | // If the timecode is off by a frame (dropped frame) wait to sync |
2538 | 2582 | if (delta > (int) frame_interval / 1200 && |
… |
… |
void NuppelVideoPlayer::AVSync(void) |
2540 | 2584 | prevrp == 0) |
2541 | 2585 | { |
2542 | 2586 | //cerr << "+ "; |
| 2587 | VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, QString("A/V delay %1").arg(delta)); |
2543 | 2588 | videosync->AdvanceTrigger(); |
2544 | 2589 | if (m_double_framerate) |
2545 | 2590 | videosync->AdvanceTrigger(); |
… |
… |
void NuppelVideoPlayer::AVSync(void) |
2558 | 2603 | { |
2559 | 2604 | if (avsync_avg > frame_interval * 3 / 2) |
2560 | 2605 | { |
2561 | | avsync_adjustment = refreshrate; |
| 2606 | avsync_adjustment += refreshrate; |
2562 | 2607 | lastsync = true; |
| 2608 | VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, "A/V avg high extend"); |
2563 | 2609 | } |
2564 | 2610 | else if (avsync_avg < 0 - frame_interval * 3 / 2) |
2565 | 2611 | { |
2566 | | avsync_adjustment = -refreshrate; |
| 2612 | avsync_adjustment += -refreshrate; |
2567 | 2613 | lastsync = true; |
| 2614 | VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, "A/V avg high skip"); |
2568 | 2615 | } |
2569 | 2616 | } |
2570 | 2617 | else |
… |
… |
void NuppelVideoPlayer::AVSync(void) |
2574 | 2621 | { |
2575 | 2622 | avsync_avg = 0; |
2576 | 2623 | avsync_oldavg = 0; |
| 2624 | avsync_predictor = 0; |
2577 | 2625 | } |
2578 | 2626 | } |
2579 | 2627 | else |
… |
… |
void NuppelVideoPlayer::DoPause(void) |
4224 | 4272 | } |
4225 | 4273 | |
4226 | 4274 | float temp_speed = audio_stretchfactor; |
4227 | | frame_interval = (int)(1000000.0 * ffrew_skip / video_frame_rate / temp_speed); |
| 4275 | SetFrameInterval(m_scan, ffrew_skip / (video_frame_rate * temp_speed)); |
4228 | 4276 | VERBOSE(VB_PLAYBACK, QString("rate: %1 speed: %2 skip: %3 = interval %4") |
4229 | 4277 | .arg(video_frame_rate).arg(temp_speed) |
4230 | 4278 | .arg(ffrew_skip).arg(frame_interval)); |
… |
… |
void NuppelVideoPlayer::DoPlay(void) |
4286 | 4334 | ClearAfterSeek(); |
4287 | 4335 | } |
4288 | 4336 | |
4289 | | frame_interval = (int) (1000000.0f * ffrew_skip / video_frame_rate / |
4290 | | play_speed); |
| 4337 | SetFrameInterval(m_scan, ffrew_skip / (video_frame_rate * play_speed)); |
4291 | 4338 | |
4292 | 4339 | VERBOSE(VB_PLAYBACK, LOC + "DoPlay: " + |
4293 | 4340 | QString("rate: %1 speed: %2 skip: %3 => new interval %4") |
diff --git a/mythtv/libs/libmythtv/NuppelVideoPlayer.h b/mythtv/libs/libmythtv/NuppelVideoPlayer.h
index d19ff73..57fab97 100644
a
|
b
|
class MPUBLIC NuppelVideoPlayer : public CC608Reader, public CC708Reader |
519 | 519 | float WarpFactor(void); |
520 | 520 | void WrapTimecode(long long &timecode, TCTypes tc_type); |
521 | 521 | void InitAVSync(void); |
| 522 | void SetFrameInterval(FrameScanType scan, double speed); |
522 | 523 | void AVSync(void); |
523 | 524 | void FallbackDeint(void); |
524 | 525 | void CheckExtraAudioDecode(void); |
… |
… |
class MPUBLIC NuppelVideoPlayer : public CC608Reader, public CC708Reader |
805 | 806 | int avsync_adjustment; |
806 | 807 | int avsync_avg; |
807 | 808 | int avsync_oldavg; |
| 809 | bool usesmoothsync; |
| 810 | int avsync_predictor; |
| 811 | bool avsync_predictor_enabled; |
808 | 812 | int refreshrate; |
809 | 813 | bool lastsync; |
810 | 814 | bool m_playing_slower; |
diff --git a/mythtv/libs/libmythtv/vsync.cpp b/mythtv/libs/libmythtv/vsync.cpp
index 060402d..2d9691d 100644
a
|
b
|
void DRMVideoSync::WaitForFrame(int sync_delay) |
360 | 360 | if (m_delay > 0) |
361 | 361 | { |
362 | 362 | // Wait for any remaining retrace intervals in one pass. |
363 | | int n = m_delay / m_refresh_interval + 1; |
| 363 | int n = (m_delay + m_refresh_interval - 1) / m_refresh_interval; |
364 | 364 | |
365 | 365 | drm_wait_vblank_t blank; |
366 | 366 | blank.request.type = DRM_VBLANK_RELATIVE; |
… |
… |
void OpenGLVideoSync::WaitForFrame(int sync_delay) |
533 | 533 | // Wait for any remaining retrace intervals in one pass. |
534 | 534 | if (m_delay > 0) |
535 | 535 | { |
536 | | uint n = m_delay / m_refresh_interval + 1; |
| 536 | uint n = (m_delay + m_refresh_interval - 1) / m_refresh_interval; |
537 | 537 | err = gMythGLXWaitVideoSyncSGI((n+1), (frameNum+n)%(n+1), &frameNum); |
538 | 538 | checkGLSyncError(msg2, err); |
539 | 539 | m_delay = CalcDelay(); |