diff --git a/mythtv/libs/libmythtv/mythplayer.cpp b/mythtv/libs/libmythtv/mythplayer.cpp
index 0e0e208..b6dad9e 100644
a
|
b
|
void MythPlayer::InitAVSync(void) |
1682 | 1682 | } |
1683 | 1683 | } |
1684 | 1684 | |
| 1685 | int64_t MythPlayer::AVSyncGetAudiotime(void) |
| 1686 | { |
| 1687 | int64_t currentaudiotime = 0; |
| 1688 | if (normal_speed) |
| 1689 | { |
| 1690 | currentaudiotime = audio.GetAudioTime(); |
| 1691 | } |
| 1692 | return currentaudiotime; |
| 1693 | } |
| 1694 | |
1685 | 1695 | #define MAXDIVERGE 3.0f |
1686 | 1696 | #define DIVERGELIMIT 30.0f |
1687 | 1697 | void MythPlayer::AVSync(VideoFrame *buffer, bool limit_delay) |
… |
… |
void MythPlayer::AVSync(VideoFrame *buffer, bool limit_delay) |
1698 | 1708 | |
1699 | 1709 | float diverge = 0.0f; |
1700 | 1710 | int frameDelay = m_double_framerate ? frame_interval / 2 : frame_interval; |
| 1711 | int vsync_delay_clock = 0; |
| 1712 | int64_t currentaudiotime = 0; |
1701 | 1713 | |
1702 | 1714 | // attempt to reduce fps for standalone PIP |
1703 | 1715 | if (player_ctx->IsPIP() && framesPlayed % 2) |
… |
… |
void MythPlayer::AVSync(VideoFrame *buffer, bool limit_delay) |
1758 | 1770 | // Reset A/V Sync |
1759 | 1771 | lastsync = true; |
1760 | 1772 | |
| 1773 | currentaudiotime = AVSyncGetAudiotime(); |
1761 | 1774 | if (!using_null_videoout && |
1762 | 1775 | videoOutput->hasHWAcceleration() && |
1763 | 1776 | !videoOutput->IsSyncLocked()) |
… |
… |
void MythPlayer::AVSync(VideoFrame *buffer, bool limit_delay) |
1786 | 1799 | osdLock.unlock(); |
1787 | 1800 | VERBOSE(VB_PLAYBACK|VB_TIMESTAMP, LOC + QString("AVSync waitforframe %1 %2") |
1788 | 1801 | .arg(avsync_adjustment).arg(m_double_framerate)); |
1789 | | videosync->WaitForFrame(frameDelay + avsync_adjustment + repeat_delay); |
| 1802 | vsync_delay_clock = videosync->WaitForFrame |
| 1803 | (frameDelay + avsync_adjustment + repeat_delay); |
| 1804 | currentaudiotime = AVSyncGetAudiotime(); |
1790 | 1805 | VERBOSE(VB_PLAYBACK|VB_TIMESTAMP, LOC + "AVSync show"); |
1791 | 1806 | videoOutput->Show(ps); |
1792 | 1807 | |
… |
… |
void MythPlayer::AVSync(VideoFrame *buffer, bool limit_delay) |
1815 | 1830 | videoOutput->PrepareFrame(buffer, ps, osd); |
1816 | 1831 | osdLock.unlock(); |
1817 | 1832 | // Display the second field |
1818 | | videosync->WaitForFrame(frameDelay + avsync_adjustment); |
| 1833 | vsync_delay_clock = videosync->WaitForFrame(frameDelay + avsync_adjustment); |
1819 | 1834 | videoOutput->Show(ps); |
1820 | 1835 | } |
1821 | 1836 | |
… |
… |
void MythPlayer::AVSync(VideoFrame *buffer, bool limit_delay) |
1827 | 1842 | } |
1828 | 1843 | else |
1829 | 1844 | { |
1830 | | videosync->WaitForFrame(frameDelay); |
| 1845 | vsync_delay_clock = videosync->WaitForFrame(frameDelay); |
| 1846 | currentaudiotime = AVSyncGetAudiotime(); |
1831 | 1847 | } |
1832 | 1848 | |
1833 | 1849 | if (output_jmeter) |
1834 | | output_jmeter->RecordCycleTime(); |
1835 | | |
| 1850 | { |
| 1851 | if (output_jmeter->RecordCycleTime()) |
| 1852 | { |
| 1853 | VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, LOC + QString("A/V avsync_delay: %1, " |
| 1854 | "avsync_avg: %2") |
| 1855 | .arg(avsync_delay / 1000).arg(avsync_avg / 1000) |
| 1856 | ); |
| 1857 | } |
| 1858 | } |
1836 | 1859 | avsync_adjustment = 0; |
1837 | 1860 | |
1838 | 1861 | if (diverge > MAXDIVERGE) |
1839 | 1862 | { |
1840 | 1863 | // If audio is way behind of video, adjust for it... |
1841 | 1864 | // by cutting the frame rate in half for the length of this frame |
1842 | | avsync_adjustment = refreshrate; |
| 1865 | avsync_adjustment = frame_interval; |
1843 | 1866 | lastsync = true; |
1844 | 1867 | VERBOSE(VB_PLAYBACK, LOC + |
1845 | 1868 | QString("Video is %1 frames ahead of audio,\n" |
… |
… |
void MythPlayer::AVSync(VideoFrame *buffer, bool limit_delay) |
1848 | 1871 | |
1849 | 1872 | if (audio.HasAudioOut() && normal_speed) |
1850 | 1873 | { |
| 1874 | // must be sampled here due to Show delays |
1851 | 1875 | int64_t currentaudiotime = audio.GetAudioTime(); |
1852 | | VERBOSE(VB_TIMESTAMP, LOC + QString( |
| 1876 | VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, LOC + QString( |
1853 | 1877 | "A/V timecodes audio %1 video %2 frameinterval %3 " |
1854 | 1878 | "avdel %4 avg %5 tcoffset %6 " |
1855 | | "avp %7 avpen %8") |
| 1879 | "avp %7 avpen %8 avdc %9") |
1856 | 1880 | .arg(currentaudiotime) |
1857 | 1881 | .arg(timecode) |
1858 | 1882 | .arg(frame_interval) |
1859 | | .arg(timecode - currentaudiotime) |
| 1883 | .arg(timecode - currentaudiotime - (int)(vsync_delay_clock*audio.GetStretchFactor()+500)/1000) |
1860 | 1884 | .arg(avsync_avg) |
1861 | 1885 | .arg(tc_wrap[TC_AUDIO]) |
1862 | 1886 | .arg(avsync_predictor) |
1863 | 1887 | .arg(avsync_predictor_enabled) |
| 1888 | .arg(vsync_delay_clock) |
1864 | 1889 | ); |
1865 | 1890 | if (currentaudiotime != 0 && timecode != 0) |
1866 | 1891 | { // currentaudiotime == 0 after a seek |
1867 | 1892 | // The time at the start of this frame (ie, now) is given by |
1868 | 1893 | // last->timecode |
1869 | | int delta = (int)((timecode - prevtc)/play_speed) - (frame_interval / 1000); |
1870 | | prevtc = timecode; |
1871 | | //cerr << delta << " "; |
1872 | | |
1873 | | // If the timecode is off by a frame (dropped frame) wait to sync |
1874 | | if (delta > (int) frame_interval / 1200 && |
1875 | | delta < (int) frame_interval / 1000 * 3 && |
1876 | | prevrp == 0) |
| 1894 | if (prevtc != 0) |
1877 | 1895 | { |
1878 | | // wait an extra frame interval |
1879 | | avsync_adjustment += frame_interval; |
| 1896 | int delta = (int)((timecode - prevtc)/play_speed) - (frame_interval / 1000); |
| 1897 | // If the timecode is off by a frame (dropped frame) wait to sync |
| 1898 | if (delta > (int) frame_interval / 1200 && |
| 1899 | delta < (int) frame_interval / 1000 * 3 && |
| 1900 | prevrp == 0) |
| 1901 | { |
| 1902 | // wait an extra frame interval |
| 1903 | VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, LOC + QString("A/V delay %1").arg(delta)); |
| 1904 | avsync_adjustment += frame_interval; |
| 1905 | } |
1880 | 1906 | } |
| 1907 | prevtc = timecode; |
1881 | 1908 | prevrp = repeat_pict; |
1882 | 1909 | |
1883 | | avsync_delay = (timecode - currentaudiotime) * 1000;//usec |
| 1910 | avsync_delay = (timecode - currentaudiotime) * 1000 - (int)(vsync_delay_clock*audio.GetStretchFactor()); //usec |
1884 | 1911 | // prevents major jitter when pts resets during dvd title |
1885 | 1912 | if (avsync_delay > 2000000 && limit_delay) |
1886 | 1913 | avsync_delay = 90000; |
1887 | 1914 | avsync_avg = (avsync_delay + (avsync_avg * 3)) / 4; |
1888 | 1915 | |
| 1916 | int avsync_used = avsync_avg; |
| 1917 | if (labs(avsync_used) > labs(avsync_delay)) |
| 1918 | avsync_used = avsync_delay; |
| 1919 | |
1889 | 1920 | /* If the audio time codes and video diverge, shift |
1890 | 1921 | the video by one interlaced field (1/2 frame) */ |
1891 | 1922 | if (!lastsync) |
1892 | 1923 | { |
1893 | | if (avsync_avg > frame_interval * 3 / 2) |
| 1924 | if (avsync_used > refreshrate) |
1894 | 1925 | { |
1895 | 1926 | avsync_adjustment += refreshrate; |
1896 | | lastsync = true; |
| 1927 | VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, LOC + "A/V avg high extend"); |
1897 | 1928 | } |
1898 | | else if (avsync_avg < 0 - frame_interval * 3 / 2) |
| 1929 | else if (avsync_avg < 0 - refreshrate) |
1899 | 1930 | { |
1900 | 1931 | avsync_adjustment -= refreshrate; |
1901 | | lastsync = true; |
| 1932 | VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, LOC + "A/V avg high skip"); |
1902 | 1933 | } |
1903 | 1934 | } |
1904 | 1935 | else |
… |
… |
void MythPlayer::AVSync(VideoFrame *buffer, bool limit_delay) |
1909 | 1940 | ResetAVSync(); |
1910 | 1941 | } |
1911 | 1942 | } |
| 1943 | else |
| 1944 | { |
| 1945 | VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, LOC + QString("A/V no sync proc ns:%1").arg(normal_speed)); |
| 1946 | } |
1912 | 1947 | } |
1913 | 1948 | |
1914 | 1949 | void MythPlayer::RefreshPauseFrame(void) |
diff --git a/mythtv/libs/libmythtv/mythplayer.h b/mythtv/libs/libmythtv/mythplayer.h
index c4744bf..293f8ac 100644
a
|
b
|
class MPUBLIC MythPlayer |
514 | 514 | void InitAVSync(void); |
515 | 515 | virtual void AVSync(VideoFrame *buffer, bool limit_delay = false); |
516 | 516 | void ResetAVSync(void); |
| 517 | int64_t AVSyncGetAudiotime(void); |
517 | 518 | void SetFrameInterval(FrameScanType scan, double speed); |
518 | 519 | void FallbackDeint(void); |
519 | 520 | void CheckExtraAudioDecode(void); |
diff --git a/mythtv/libs/libmythtv/vsync.cpp b/mythtv/libs/libmythtv/vsync.cpp
index 6e1b891..9d0a005 100644
a
|
b
|
int VideoSync::CalcDelay() |
178 | 178 | m_nexttrigger = now + ret_val; |
179 | 179 | } |
180 | 180 | |
181 | | if (ret_val < -m_frame_interval) |
| 181 | if (ret_val < -m_frame_interval && m_frame_interval >= m_refresh_interval) |
182 | 182 | { |
183 | 183 | ret_val = -m_frame_interval; |
184 | 184 | |
… |
… |
void DRMVideoSync::Start(void) |
293 | 293 | VideoSync::Start(); |
294 | 294 | } |
295 | 295 | |
296 | | void DRMVideoSync::WaitForFrame(int sync_delay) |
| 296 | int DRMVideoSync::WaitForFrame(int sync_delay) |
297 | 297 | { |
298 | 298 | // Offset for externally-provided A/V sync delay |
299 | 299 | m_nexttrigger += sync_delay; |
… |
… |
void DRMVideoSync::WaitForFrame(int sync_delay) |
316 | 316 | if (m_delay > 0) |
317 | 317 | { |
318 | 318 | // Wait for any remaining retrace intervals in one pass. |
319 | | int n = m_delay / m_refresh_interval + 1; |
| 319 | int n = (m_delay + m_refresh_interval - 1) / m_refresh_interval; |
320 | 320 | |
321 | 321 | drm_wait_vblank_t blank; |
322 | 322 | blank.request.type = DRM_VBLANK_RELATIVE; |
… |
… |
void DRMVideoSync::WaitForFrame(int sync_delay) |
327 | 327 | //cerr << " Delay " << m_delay << endl; |
328 | 328 | } |
329 | 329 | |
| 330 | return m_delay; |
| 331 | |
330 | 332 | KeepPhase(); |
331 | 333 | } |
332 | 334 | #endif /* !_WIN32 */ |
… |
… |
void OpenGLVideoSync::Start(void) |
397 | 399 | #endif /* USING_OPENGL_VSYNC */ |
398 | 400 | } |
399 | 401 | |
400 | | void OpenGLVideoSync::WaitForFrame(int sync_delay) |
| 402 | int OpenGLVideoSync::WaitForFrame(int sync_delay) |
401 | 403 | { |
402 | 404 | (void) sync_delay; |
403 | 405 | #ifdef USING_OPENGL_VSYNC |
… |
… |
void OpenGLVideoSync::WaitForFrame(int sync_delay) |
409 | 411 | m_delay = CalcDelay(); |
410 | 412 | if (m_delay > 0) |
411 | 413 | usleep(m_delay); |
412 | | return; |
| 414 | return 0; |
413 | 415 | } |
414 | 416 | |
415 | 417 | if (!m_context) |
416 | | return; |
| 418 | return 0; |
417 | 419 | |
418 | 420 | unsigned int frameNum = m_context->GetVideoSyncCount(); |
419 | 421 | |
… |
… |
void OpenGLVideoSync::WaitForFrame(int sync_delay) |
427 | 429 | // Wait for any remaining retrace intervals in one pass. |
428 | 430 | if (m_delay > 0) |
429 | 431 | { |
430 | | uint n = m_delay / m_refresh_interval + 1; |
| 432 | uint n = (m_delay + m_refresh_interval - 1) / m_refresh_interval; |
431 | 433 | m_context->WaitForVideoSync((n+1), (frameNum+n)%(n+1), &frameNum); |
432 | 434 | m_delay = CalcDelay(); |
433 | 435 | } |
434 | 436 | |
| 437 | return m_delay; |
| 438 | |
435 | 439 | KeepPhase(); |
436 | 440 | #endif /* USING_OPENGL_VSYNC */ |
437 | 441 | } |
… |
… |
bool RTCVideoSync::TryInit(void) |
479 | 483 | return true; |
480 | 484 | } |
481 | 485 | |
482 | | void RTCVideoSync::WaitForFrame(int sync_delay) |
| 486 | int RTCVideoSync::WaitForFrame(int sync_delay) |
483 | 487 | { |
484 | 488 | m_nexttrigger += sync_delay; |
485 | 489 | |
… |
… |
void RTCVideoSync::WaitForFrame(int sync_delay) |
494 | 498 | if ((val < 0) && (m_delay > 0)) |
495 | 499 | usleep(m_delay); |
496 | 500 | } |
| 501 | return 0; |
497 | 502 | } |
498 | 503 | #endif /* __linux__ */ |
499 | 504 | |
… |
… |
bool VDPAUVideoSync::TryInit(void) |
517 | 522 | return true; |
518 | 523 | } |
519 | 524 | |
520 | | void VDPAUVideoSync::WaitForFrame(int sync_delay) |
| 525 | int VDPAUVideoSync::WaitForFrame(int sync_delay) |
521 | 526 | { |
522 | 527 | // Offset for externally-provided A/V sync delay |
523 | 528 | m_nexttrigger += sync_delay; |
… |
… |
void VDPAUVideoSync::WaitForFrame(int sync_delay) |
528 | 533 | |
529 | 534 | VideoOutputVDPAU *vo = (VideoOutputVDPAU *)(m_video_output); |
530 | 535 | vo->SetNextFrameDisplayTimeOffset(m_delay); |
| 536 | return 0; |
531 | 537 | } |
532 | 538 | #endif |
533 | 539 | |
… |
… |
bool BusyWaitVideoSync::TryInit(void) |
548 | 554 | return true; |
549 | 555 | } |
550 | 556 | |
551 | | void BusyWaitVideoSync::WaitForFrame(int sync_delay) |
| 557 | int BusyWaitVideoSync::WaitForFrame(int sync_delay) |
552 | 558 | { |
553 | 559 | // Offset for externally-provided A/V sync delay |
554 | 560 | m_nexttrigger += sync_delay; |
… |
… |
void BusyWaitVideoSync::WaitForFrame(int sync_delay) |
577 | 583 | if (cnt > 1) |
578 | 584 | m_cheat -= 200; |
579 | 585 | } |
| 586 | return 0; |
580 | 587 | } |
581 | 588 | |
582 | 589 | USleepVideoSync::USleepVideoSync(VideoOutput *vo, |
… |
… |
bool USleepVideoSync::TryInit(void) |
594 | 601 | return true; |
595 | 602 | } |
596 | 603 | |
597 | | void USleepVideoSync::WaitForFrame(int sync_delay) |
| 604 | int USleepVideoSync::WaitForFrame(int sync_delay) |
598 | 605 | { |
599 | 606 | // Offset for externally-provided A/V sync delay |
600 | 607 | m_nexttrigger += sync_delay; |
… |
… |
void USleepVideoSync::WaitForFrame(int sync_delay) |
602 | 609 | m_delay = CalcDelay(); |
603 | 610 | if (m_delay > 0) |
604 | 611 | usleep(m_delay); |
| 612 | return 0; |
605 | 613 | } |
606 | 614 | |
diff --git a/mythtv/libs/libmythtv/vsync.h b/mythtv/libs/libmythtv/vsync.h
index 310d866..3db298b 100644
a
|
b
|
class VideoSync |
64 | 64 | virtual void Start(void); |
65 | 65 | |
66 | 66 | /** \brief Waits for next a frame or field. |
| 67 | * Returns delay to real frame timing in usec |
67 | 68 | * |
68 | 69 | * Start(void), WaitForFrame(void), and Stop(void) should |
69 | 70 | * always be called from same thread, to prevent bad |
… |
… |
class VideoSync |
72 | 73 | * \param sync_delay time until the desired frame or field |
73 | 74 | * \sa CalcDelay(void), KeepPhase(void) |
74 | 75 | */ |
75 | | virtual void WaitForFrame(int sync_delay) = 0; |
| 76 | virtual int WaitForFrame(int sync_delay) = 0; |
76 | 77 | |
77 | 78 | /// \brief Returns the (minimum) refresh interval of the output device. |
78 | 79 | int getRefreshInterval(void) const { return m_refresh_interval; } |
… |
… |
class DRMVideoSync : public VideoSync |
123 | 124 | QString getName(void) const { return QString("DRM"); } |
124 | 125 | bool TryInit(void); |
125 | 126 | void Start(void); |
126 | | void WaitForFrame(int sync_delay); |
| 127 | int WaitForFrame(int sync_delay); |
127 | 128 | |
128 | 129 | private: |
129 | 130 | int m_dri_fd; |
… |
… |
class OpenGLVideoSync : public VideoSync |
164 | 165 | QString getName(void) const { return QString("SGI OpenGL"); } |
165 | 166 | bool TryInit(void); |
166 | 167 | void Start(void); |
167 | | void WaitForFrame(int sync_delay); |
| 168 | int WaitForFrame(int sync_delay); |
168 | 169 | |
169 | 170 | private: |
170 | 171 | MythRenderOpenGL *m_context; |
… |
… |
class RTCVideoSync : public VideoSync |
192 | 193 | |
193 | 194 | QString getName(void) const { return QString("RTC"); } |
194 | 195 | bool TryInit(void); |
195 | | void WaitForFrame(int sync_delay); |
| 196 | int WaitForFrame(int sync_delay); |
196 | 197 | |
197 | 198 | private: |
198 | 199 | int m_rtcfd; |
… |
… |
class VDPAUVideoSync : public VideoSync |
212 | 213 | |
213 | 214 | QString getName(void) const { return QString("VDPAU"); } |
214 | 215 | bool TryInit(void); |
215 | | void WaitForFrame(int sync_delay); |
| 216 | int WaitForFrame(int sync_delay); |
216 | 217 | |
217 | 218 | private: |
218 | 219 | }; |
… |
… |
class BusyWaitVideoSync : public VideoSync |
239 | 240 | |
240 | 241 | QString getName(void) const { return QString("USleep with busy wait"); } |
241 | 242 | bool TryInit(void); |
242 | | void WaitForFrame(int sync_delay); |
| 243 | int WaitForFrame(int sync_delay); |
243 | 244 | |
244 | 245 | private: |
245 | 246 | int m_cheat; |
… |
… |
class USleepVideoSync : public VideoSync |
266 | 267 | |
267 | 268 | QString getName(void) const { return QString("USleep"); } |
268 | 269 | bool TryInit(void); |
269 | | void WaitForFrame(int sync_delay); |
| 270 | int WaitForFrame(int sync_delay); |
270 | 271 | }; |
271 | 272 | #endif /* VSYNC_H_INCLUDED */ |