|
MythTV
0.26-pre
|
00001 /* 00002 * Copyright (c) 2004 Doug Larrick <doug@ties.org>. 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation; either version 2, or (at your option) 00007 * any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License 00015 * along with this program; if not, write to the Free Software Foundation, 00016 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 00017 */ 00018 00019 #include <cstdio> 00020 #include <cerrno> 00021 #include <cmath> 00022 #include <cstdlib> // for abs(int) 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 // m_forceskip allows for skipping one sync method 00084 // due to crash on the previous run. 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 // TESTVIDEOSYNC(VDPAUVideoSync); 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 // set nexttrigger to our new target time 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 // set nexttrigger to our new target time 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; // couldn't open device 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; // VBLANK ioctl didn't worko 00277 } 00278 00279 return true; 00280 } 00281 00282 void DRMVideoSync::Start(void) 00283 { 00284 // Wait for a refresh so we start out synched 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 // Offset for externally-provided A/V sync delay 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 // Always sync to the next retrace execpt when we are very late. 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 // Wait for any remaining retrace intervals in one pass. 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 /* !_WIN32 */ 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 // FIXME, does it make sense to tie RTCRATE to the desired framerate? 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 /* __linux__ */ 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 // Offset for externally-provided A/V sync delay 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 // The usleep() is shortened by "cheat" so that this process gets 00426 // the CPU early for about half the frames. 00427 if (m_delay > (m_cheat - m_fudge)) 00428 usleep(m_delay - (m_cheat - m_fudge)); 00429 00430 // If late, draw the frame ASAP. If early, hold the CPU until 00431 // half as late as the previous frame (fudge). 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 // Offset for externally-provided A/V sync delay 00464 m_nexttrigger += sync_delay; 00465 00466 m_delay = CalcDelay(); 00467 if (m_delay > 0) 00468 usleep(m_delay); 00469 return 0; 00470 } 00471
1.7.6.1