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