1 /* 2 * libjingle 3 * Copyright 2011 Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 // Implementation of CarbonVideoRenderer 29 30 #include "talk/media/devices/carbonvideorenderer.h" 31 32 #include "talk/media/base/videocommon.h" 33 #include "talk/media/base/videoframe.h" 34 #include "webrtc/base/logging.h" 35 36 namespace cricket { 37 38 CarbonVideoRenderer::CarbonVideoRenderer(int x, int y) 39 : image_width_(0), 40 image_height_(0), 41 x_(x), 42 y_(y), 43 window_ref_(NULL) { 44 } 45 46 CarbonVideoRenderer::~CarbonVideoRenderer() { 47 if (window_ref_) { 48 DisposeWindow(window_ref_); 49 } 50 } 51 52 // Called from the main event loop. All renderering needs to happen on 53 // the main thread. 54 OSStatus CarbonVideoRenderer::DrawEventHandler(EventHandlerCallRef handler, 55 EventRef event, 56 void* data) { 57 OSStatus status = noErr; 58 CarbonVideoRenderer* renderer = static_cast<CarbonVideoRenderer*>(data); 59 if (renderer != NULL) { 60 if (!renderer->DrawFrame()) { 61 LOG(LS_ERROR) << "Failed to draw frame."; 62 } 63 } 64 return status; 65 } 66 67 bool CarbonVideoRenderer::DrawFrame() { 68 // Grab the image lock to make sure it is not changed why we'll draw it. 69 rtc::CritScope cs(&image_crit_); 70 71 if (image_.get() == NULL) { 72 // Nothing to draw, just return. 73 return true; 74 } 75 int width = image_width_; 76 int height = image_height_; 77 CGDataProviderRef provider = 78 CGDataProviderCreateWithData(NULL, image_.get(), width * height * 4, 79 NULL); 80 CGColorSpaceRef color_space_ref = CGColorSpaceCreateDeviceRGB(); 81 CGBitmapInfo bitmap_info = kCGBitmapByteOrderDefault; 82 CGColorRenderingIntent rendering_intent = kCGRenderingIntentDefault; 83 CGImageRef image_ref = CGImageCreate(width, height, 8, 32, width * 4, 84 color_space_ref, bitmap_info, provider, 85 NULL, false, rendering_intent); 86 CGDataProviderRelease(provider); 87 88 if (image_ref == NULL) { 89 return false; 90 } 91 CGContextRef context; 92 SetPortWindowPort(window_ref_); 93 if (QDBeginCGContext(GetWindowPort(window_ref_), &context) != noErr) { 94 CGImageRelease(image_ref); 95 return false; 96 } 97 Rect window_bounds; 98 GetWindowPortBounds(window_ref_, &window_bounds); 99 100 // Anchor the image to the top left corner. 101 int x = 0; 102 int y = window_bounds.bottom - CGImageGetHeight(image_ref); 103 CGRect dst_rect = CGRectMake(x, y, CGImageGetWidth(image_ref), 104 CGImageGetHeight(image_ref)); 105 CGContextDrawImage(context, dst_rect, image_ref); 106 CGContextFlush(context); 107 QDEndCGContext(GetWindowPort(window_ref_), &context); 108 CGImageRelease(image_ref); 109 return true; 110 } 111 112 bool CarbonVideoRenderer::SetSize(int width, int height, int reserved) { 113 if (width != image_width_ || height != image_height_) { 114 // Grab the image lock while changing its size. 115 rtc::CritScope cs(&image_crit_); 116 image_width_ = width; 117 image_height_ = height; 118 image_.reset(new uint8_t[width * height * 4]); 119 memset(image_.get(), 255, width * height * 4); 120 } 121 return true; 122 } 123 124 bool CarbonVideoRenderer::RenderFrame(const VideoFrame* video_frame) { 125 if (!video_frame) { 126 return false; 127 } 128 { 129 const VideoFrame* frame = video_frame->GetCopyWithRotationApplied(); 130 131 if (!SetSize(frame->GetWidth(), frame->GetHeight(), 0)) { 132 return false; 133 } 134 135 // Grab the image lock so we are not trashing up the image being drawn. 136 rtc::CritScope cs(&image_crit_); 137 frame->ConvertToRgbBuffer(cricket::FOURCC_ABGR, 138 image_.get(), 139 frame->GetWidth() * frame->GetHeight() * 4, 140 frame->GetWidth() * 4); 141 } 142 143 // Trigger a repaint event for the whole window. 144 Rect bounds; 145 InvalWindowRect(window_ref_, GetWindowPortBounds(window_ref_, &bounds)); 146 return true; 147 } 148 149 bool CarbonVideoRenderer::Initialize() { 150 OSStatus err; 151 WindowAttributes attributes = 152 kWindowStandardDocumentAttributes | 153 kWindowLiveResizeAttribute | 154 kWindowFrameworkScaledAttribute | 155 kWindowStandardHandlerAttribute; 156 157 struct Rect bounds; 158 bounds.top = y_; 159 bounds.bottom = 480; 160 bounds.left = x_; 161 bounds.right = 640; 162 err = CreateNewWindow(kDocumentWindowClass, attributes, 163 &bounds, &window_ref_); 164 if (!window_ref_ || err != noErr) { 165 LOG(LS_ERROR) << "CreateNewWindow failed, error code: " << err; 166 return false; 167 } 168 static const EventTypeSpec event_spec = { 169 kEventClassWindow, 170 kEventWindowDrawContent 171 }; 172 173 err = InstallWindowEventHandler( 174 window_ref_, 175 NewEventHandlerUPP(CarbonVideoRenderer::DrawEventHandler), 176 GetEventTypeCount(event_spec), 177 &event_spec, 178 this, 179 NULL); 180 if (err != noErr) { 181 LOG(LS_ERROR) << "Failed to install event handler, error code: " << err; 182 return false; 183 } 184 SelectWindow(window_ref_); 185 ShowWindow(window_ref_); 186 return true; 187 } 188 189 } // namespace cricket 190