1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "content/browser/renderer_host/display_link_mac.h" 6 7#include "base/debug/trace_event.h" 8#include "base/logging.h" 9 10namespace base { 11 12template<> 13struct ScopedTypeRefTraits<CVDisplayLinkRef> { 14 static void Retain(CVDisplayLinkRef object) { 15 CVDisplayLinkRetain(object); 16 } 17 static void Release(CVDisplayLinkRef object) { 18 CVDisplayLinkRelease(object); 19 } 20}; 21 22} // namespace base 23 24namespace content { 25 26// static 27scoped_refptr<DisplayLinkMac> DisplayLinkMac::GetForDisplay( 28 CGDirectDisplayID display_id) { 29 // Return the existing display link for this display, if it exists. 30 DisplayMap::iterator found = display_map_.Get().find(display_id); 31 if (found != display_map_.Get().end()) { 32 return found->second; 33 } 34 35 CVReturn ret = kCVReturnSuccess; 36 37 base::ScopedTypeRef<CVDisplayLinkRef> display_link; 38 ret = CVDisplayLinkCreateWithCGDisplay( 39 display_id, 40 display_link.InitializeInto()); 41 if (ret != kCVReturnSuccess) { 42 LOG(ERROR) << "CVDisplayLinkCreateWithActiveCGDisplays failed: " << ret; 43 return NULL; 44 } 45 46 scoped_refptr<DisplayLinkMac> display_link_mac; 47 display_link_mac = new DisplayLinkMac(display_id, display_link); 48 49 ret = CVDisplayLinkSetOutputCallback( 50 display_link_mac->display_link_, 51 &DisplayLinkCallback, 52 display_link_mac.get()); 53 if (ret != kCVReturnSuccess) { 54 LOG(ERROR) << "CVDisplayLinkSetOutputCallback failed: " << ret; 55 return NULL; 56 } 57 58 return display_link_mac; 59} 60 61DisplayLinkMac::DisplayLinkMac( 62 CGDirectDisplayID display_id, 63 base::ScopedTypeRef<CVDisplayLinkRef> display_link) 64 : display_id_(display_id), 65 display_link_(display_link), 66 stop_timer_( 67 FROM_HERE, base::TimeDelta::FromSeconds(1), 68 this, &DisplayLinkMac::StopDisplayLink), 69 timebase_and_interval_valid_(false) { 70 DCHECK(display_map_.Get().find(display_id) == display_map_.Get().end()); 71 display_map_.Get().insert(std::make_pair(display_id_, this)); 72} 73 74DisplayLinkMac::~DisplayLinkMac() { 75 if (CVDisplayLinkIsRunning(display_link_)) 76 CVDisplayLinkStop(display_link_); 77 78 DisplayMap::iterator found = display_map_.Get().find(display_id_); 79 DCHECK(found != display_map_.Get().end()); 80 DCHECK(found->second == this); 81 display_map_.Get().erase(found); 82} 83 84bool DisplayLinkMac::GetVSyncParameters( 85 base::TimeTicks* timebase, base::TimeDelta* interval) { 86 StartOrContinueDisplayLink(); 87 88 base::AutoLock lock(lock_); 89 if (!timebase_and_interval_valid_) 90 return false; 91 92 *timebase = timebase_; 93 *interval = interval_; 94 return true; 95} 96 97void DisplayLinkMac::Tick(const CVTimeStamp* cv_time) { 98 TRACE_EVENT0("browser", "DisplayLinkMac::GetVSyncParameters"); 99 base::AutoLock lock(lock_); 100 101 // Verify that videoRefreshPeriod is 32 bits. 102 DCHECK((cv_time->videoRefreshPeriod & ~0xffffFFFFull) == 0ull); 103 104 // Verify that the numerator and denominator make some sense. 105 uint32 numerator = static_cast<uint32>(cv_time->videoRefreshPeriod); 106 uint32 denominator = cv_time->videoTimeScale; 107 if (numerator <= 0 || denominator <= 0) { 108 LOG(WARNING) << "Unexpected numerator or denominator, bailing."; 109 return; 110 } 111 112 timebase_ = base::TimeTicks::FromInternalValue( 113 cv_time->hostTime / 1000); 114 interval_ = base::TimeDelta::FromMicroseconds( 115 1000000 * static_cast<int64>(numerator) / denominator); 116 timebase_and_interval_valid_ = true; 117} 118 119void DisplayLinkMac::StartOrContinueDisplayLink() { 120 // Reset the timer, so that the display link won't be turned off for another 121 // second. 122 stop_timer_.Reset(); 123 124 if (CVDisplayLinkIsRunning(display_link_)) 125 return; 126 127 CVReturn ret = CVDisplayLinkStart(display_link_); 128 if (ret != kCVReturnSuccess) { 129 LOG(ERROR) << "CVDisplayLinkStart failed: " << ret; 130 } 131} 132 133void DisplayLinkMac::StopDisplayLink() { 134 if (!CVDisplayLinkIsRunning(display_link_)) 135 return; 136 137 CVReturn ret = CVDisplayLinkStop(display_link_); 138 if (ret != kCVReturnSuccess) { 139 LOG(ERROR) << "CVDisplayLinkStop failed: " << ret; 140 } 141} 142 143CVReturn DisplayLinkMac::DisplayLinkCallback( 144 CVDisplayLinkRef display_link, 145 const CVTimeStamp* now, 146 const CVTimeStamp* output_time, 147 CVOptionFlags flags_in, 148 CVOptionFlags* flags_out, 149 void* context) { 150 DisplayLinkMac* display_link_mac = static_cast<DisplayLinkMac*>(context); 151 display_link_mac->Tick(output_time); 152 return kCVReturnSuccess; 153} 154 155// static 156base::LazyInstance<DisplayLinkMac::DisplayMap> 157 DisplayLinkMac::display_map_ = LAZY_INSTANCE_INITIALIZER; 158 159} // content 160 161