00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include <cstdio>
00020 #include <cerrno>
00021 #include <cmath>
00022 #include <cstdlib>
00023 #include <unistd.h>
00024 #include <fcntl.h>
00025
00026 #include <sys/types.h>
00027 #include <sys/stat.h>
00028 #include "compat.h"
00029
00030 #ifndef _WIN32
00031 #include <sys/ioctl.h>
00032 #include <sys/poll.h>
00033 #endif
00034
00035 #include "mythcontext.h"
00036 #include "mythmainwindow.h"
00037
00038 #ifdef USING_XV
00039 #include "videoout_xv.h"
00040 #endif
00041
00042 #ifdef USING_VDPAU
00043 #include "videoout_vdpau.h"
00044 #endif
00045
00046 #ifdef __linux__
00047 #include <linux/rtc.h>
00048 #endif
00049
00050 using namespace std;
00051 #include "videooutbase.h"
00052 #include "vsync.h"
00053
00054 bool tryingVideoSync = false;
00055 int VideoSync::m_forceskip = 0;
00056
00057 #define TESTVIDEOSYNC(NAME) \
00058 do { if (++m_forceskip > skip) \
00059 { \
00060 trial = new NAME (video_output, frame_interval, \
00061 refresh_interval, halve_frame_interval); \
00062 if (trial->TryInit()) \
00063 { \
00064 m_forceskip = skip; \
00065 tryingVideoSync = false; \
00066 return trial; \
00067 } \
00068 delete trial; \
00069 } } while (false)
00070
00071 #define LOC QString("VSYNC: ")
00072
00076 VideoSync *VideoSync::BestMethod(VideoOutput *video_output,
00077 uint frame_interval, uint refresh_interval,
00078 bool halve_frame_interval)
00079 {
00080 VideoSync *trial = NULL;
00081 tryingVideoSync = true;
00082
00083
00084
00085 int skip = 0;
00086 if (m_forceskip)
00087 {
00088 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00089 QString("A previous trial crashed, skipping %1").arg(m_forceskip));
00090
00091 skip = m_forceskip;
00092 m_forceskip = 0;
00093 }
00094
00095 #ifdef USING_VDPAU
00096
00097 #endif
00098 #ifndef _WIN32
00099 TESTVIDEOSYNC(DRMVideoSync);
00100 #endif // _WIN32
00101 #ifdef __linux__
00102 TESTVIDEOSYNC(RTCVideoSync);
00103 #endif // __linux__
00104
00105 TESTVIDEOSYNC(BusyWaitVideoSync);
00106
00107 tryingVideoSync=false;
00108 return NULL;
00109 }
00110
00115 VideoSync::VideoSync(VideoOutput *video_output,
00116 int frameint, int refreshint,
00117 bool halve_frame_interval) :
00118 m_video_output(video_output), m_frame_interval(frameint),
00119 m_refresh_interval(refreshint), m_interlaced(halve_frame_interval),
00120 m_nexttrigger(0), m_delay(-1)
00121 {
00122 }
00123
00124 int64_t VideoSync::GetTime(void)
00125 {
00126 struct timeval now_tv;
00127 gettimeofday(&now_tv, NULL);
00128 return now_tv.tv_sec * 1000000LL + now_tv.tv_usec;
00129 }
00130
00131 void VideoSync::Start(void)
00132 {
00133 m_nexttrigger = GetTime();
00134 }
00135
00147 int VideoSync::CalcDelay()
00148 {
00149 int64_t now = GetTime();
00150 #if 0
00151 LOG(VB_GENERAL, LOG_DEBUG, QString("CalcDelay: next: %1 now %2")
00152 .arg(timeval_str(m_nexttrigger)) .arg(timeval_str(now)));
00153 #endif
00154
00155 int ret_val = m_nexttrigger - now;
00156
00157 #if 0
00158 LOG(VB_GENERAL, LOG_DEBUG, QString("delay %1").arg(ret_val);
00159 #endif
00160
00161 if (ret_val > m_frame_interval * 4)
00162 {
00163 if (m_interlaced)
00164 ret_val = (m_frame_interval / 2) * 4;
00165 else
00166 ret_val = m_frame_interval * 4;
00167
00168
00169 m_nexttrigger = now + ret_val;
00170 }
00171
00172 if (ret_val < -m_frame_interval && m_frame_interval >= m_refresh_interval)
00173 {
00174 ret_val = -m_frame_interval;
00175
00176
00177 m_nexttrigger = now + ret_val;
00178 }
00179
00180 return ret_val;
00181 }
00182
00192 void VideoSync::KeepPhase()
00193 {
00194 #if 0
00195 LOG(VB_GENERAL, LOG_DEBUG, QString("%1").arg(m_delay));
00196 #endif
00197 if (m_delay < -(m_refresh_interval/2))
00198 m_nexttrigger += 200;
00199 else if (m_delay > -500)
00200 m_nexttrigger += -2000;
00201 }
00202
00203 #ifndef _WIN32
00204 #define DRM_VBLANK_RELATIVE 0x1;
00205
00206 struct drm_wait_vblank_request {
00207 int type;
00208 unsigned int sequence;
00209 unsigned long signal;
00210 };
00211
00212 struct drm_wait_vblank_reply {
00213 int type;
00214 unsigned int sequence;
00215 long tval_sec;
00216 long tval_usec;
00217 };
00218
00219 typedef union drm_wait_vblank {
00220 struct drm_wait_vblank_request request;
00221 struct drm_wait_vblank_reply reply;
00222 } drm_wait_vblank_t;
00223
00224 #define DRM_IOCTL_BASE 'd'
00225 #define DRM_IOWR(nr,type) _IOWR(DRM_IOCTL_BASE,nr,type)
00226
00227 #define DRM_IOCTL_WAIT_VBLANK DRM_IOWR(0x3a, drm_wait_vblank_t)
00228
00229 static int drmWaitVBlank(int fd, drm_wait_vblank_t *vbl)
00230 {
00231 int ret = -1;
00232
00233 do {
00234 ret = ioctl(fd, DRM_IOCTL_WAIT_VBLANK, vbl);
00235 vbl->request.type &= ~DRM_VBLANK_RELATIVE;
00236 } while (ret && errno == EINTR);
00237
00238 return ret;
00239 }
00240
00241 const char *DRMVideoSync::sm_dri_dev = "/dev/dri/card0";
00242
00243 DRMVideoSync::DRMVideoSync(VideoOutput *vo, int fr, int ri, bool intl) :
00244 VideoSync(vo, fr, ri, intl)
00245 {
00246 m_dri_fd = -1;
00247 }
00248
00249 DRMVideoSync::~DRMVideoSync()
00250 {
00251 if (m_dri_fd >= 0)
00252 close(m_dri_fd);
00253 m_dri_fd = -1;
00254 }
00255
00256 bool DRMVideoSync::TryInit(void)
00257 {
00258 drm_wait_vblank_t blank;
00259
00260 m_dri_fd = open(sm_dri_dev, O_RDWR);
00261 if (m_dri_fd < 0)
00262 {
00263 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00264 QString("DRMVideoSync: Could not open device %1, %2")
00265 .arg(sm_dri_dev).arg(strerror(errno)));
00266 return false;
00267 }
00268
00269 blank.request.type = DRM_VBLANK_RELATIVE;
00270 blank.request.sequence = 1;
00271 if (drmWaitVBlank(m_dri_fd, &blank))
00272 {
00273 LOG(VB_PLAYBACK, LOG_ERR, LOC +
00274 QString("DRMVideoSync: VBlank ioctl did not"
00275 " work, unimplemented in this driver?"));
00276 return false;
00277 }
00278
00279 return true;
00280 }
00281
00282 void DRMVideoSync::Start(void)
00283 {
00284
00285 drm_wait_vblank_t blank;
00286 blank.request.type = DRM_VBLANK_RELATIVE;
00287 blank.request.sequence = 1;
00288 drmWaitVBlank(m_dri_fd, &blank);
00289 VideoSync::Start();
00290 }
00291
00292 int DRMVideoSync::WaitForFrame(int sync_delay)
00293 {
00294
00295 m_nexttrigger += sync_delay;
00296
00297 m_delay = CalcDelay();
00298 #if 0
00299 LOG(VB_GENERAL, LOG_DEBUG, QString("WaitForFrame at : %1").arg(m_delay));
00300 #endif
00301
00302
00303 if (m_delay > -(m_refresh_interval/2))
00304 {
00305 drm_wait_vblank_t blank;
00306 blank.request.type = DRM_VBLANK_RELATIVE;
00307 blank.request.sequence = 1;
00308 drmWaitVBlank(m_dri_fd, &blank);
00309 m_delay = CalcDelay();
00310 #if 0
00311 LOG(VB_GENERAL, LOG_DEBUG, QString("Delay at sync: %1").arg(m_delay));
00312 #endif
00313 }
00314
00315 if (m_delay > 0)
00316 {
00317
00318 int n = (m_delay + m_refresh_interval - 1) / m_refresh_interval;
00319
00320 drm_wait_vblank_t blank;
00321 blank.request.type = DRM_VBLANK_RELATIVE;
00322 blank.request.sequence = n;
00323 drmWaitVBlank(m_dri_fd, &blank);
00324 m_delay = CalcDelay();
00325 #if 0
00326 LOG(VB_GENERAL, LOG_DEBUG,
00327 QString("Wait %1 intervals. Count %2 Delay %3")
00328 .arg(n) .arg(blank.request.sequence) .arg(m_delay));
00329 #endif
00330 }
00331
00332 return m_delay;
00333 }
00334 #endif
00335
00336 #ifdef __linux__
00337 #define RTCRATE 1024
00338 RTCVideoSync::RTCVideoSync(VideoOutput *vo, int fi, int ri, bool intr) :
00339 VideoSync(vo, fi, ri, intr)
00340 {
00341 m_rtcfd = -1;
00342 }
00343
00344 RTCVideoSync::~RTCVideoSync()
00345 {
00346 if (m_rtcfd >= 0)
00347 close(m_rtcfd);
00348 }
00349
00350 bool RTCVideoSync::TryInit(void)
00351 {
00352 m_rtcfd = open("/dev/rtc", O_RDONLY);
00353 if (m_rtcfd < 0)
00354 {
00355 LOG(VB_PLAYBACK, LOG_ERR, LOC +
00356 "RTCVideoSync: Could not open /dev/rtc: " + ENO);
00357 return false;
00358 }
00359
00360
00361 if ((ioctl(m_rtcfd, RTC_IRQP_SET, RTCRATE) < 0))
00362 {
00363 LOG(VB_PLAYBACK, LOG_ERR, LOC +
00364 "RTCVideoSync: Could not set RTC frequency: " + ENO);
00365 return false;
00366 }
00367
00368 if (ioctl(m_rtcfd, RTC_PIE_ON, 0) < 0)
00369 {
00370 LOG(VB_PLAYBACK, LOG_ERR, LOC +
00371 "RTCVideoSync: Could not enable periodic timer interrupts: " + ENO);
00372 return false;
00373 }
00374
00375 return true;
00376 }
00377
00378 int RTCVideoSync::WaitForFrame(int sync_delay)
00379 {
00380 m_nexttrigger += sync_delay;
00381
00382 m_delay = CalcDelay();
00383
00384 unsigned long rtcdata;
00385 while (m_delay > 0)
00386 {
00387 ssize_t val = read(m_rtcfd, &rtcdata, sizeof(rtcdata));
00388 m_delay = CalcDelay();
00389
00390 if ((val < 0) && (m_delay > 0))
00391 usleep(m_delay);
00392 }
00393 return 0;
00394 }
00395 #endif
00396
00397 BusyWaitVideoSync::BusyWaitVideoSync(VideoOutput *vo,
00398 int fr, int ri, bool intl) :
00399 VideoSync(vo, fr, ri, intl)
00400 {
00401 m_cheat = 5000;
00402 m_fudge = 0;
00403 }
00404
00405 BusyWaitVideoSync::~BusyWaitVideoSync()
00406 {
00407 }
00408
00409 bool BusyWaitVideoSync::TryInit(void)
00410 {
00411 return true;
00412 }
00413
00414 int BusyWaitVideoSync::WaitForFrame(int sync_delay)
00415 {
00416
00417 m_nexttrigger += sync_delay;
00418
00419 m_delay = CalcDelay();
00420
00421 if (m_delay > 0)
00422 {
00423 int cnt = 0;
00424 m_cheat += 100;
00425
00426
00427 if (m_delay > (m_cheat - m_fudge))
00428 usleep(m_delay - (m_cheat - m_fudge));
00429
00430
00431
00432 m_delay = CalcDelay();
00433 m_fudge = min(m_fudge, m_frame_interval);
00434 while (m_delay + m_fudge > 0)
00435 {
00436 m_delay = CalcDelay();
00437 cnt++;
00438 }
00439 m_fudge = abs(m_delay / 2);
00440 if (cnt > 1)
00441 m_cheat -= 200;
00442 }
00443 return 0;
00444 }
00445
00446 USleepVideoSync::USleepVideoSync(VideoOutput *vo,
00447 int fr, int ri, bool intl) :
00448 VideoSync(vo, fr, ri, intl)
00449 {
00450 }
00451
00452 USleepVideoSync::~USleepVideoSync()
00453 {
00454 }
00455
00456 bool USleepVideoSync::TryInit(void)
00457 {
00458 return true;
00459 }
00460
00461 int USleepVideoSync::WaitForFrame(int sync_delay)
00462 {
00463
00464 m_nexttrigger += sync_delay;
00465
00466 m_delay = CalcDelay();
00467 if (m_delay > 0)
00468 usleep(m_delay);
00469 return 0;
00470 }
00471