• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#if !defined(__has_feature) || !__has_feature(objc_arc)
12#error "This file requires ARC support."
13#endif
14
15#include "webrtc/modules/video_render/ios/video_render_ios_gles20.h"
16#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
17#include "webrtc/system_wrappers/interface/event_wrapper.h"
18#include "webrtc/system_wrappers/interface/thread_wrapper.h"
19
20using namespace webrtc;
21
22VideoRenderIosGles20::VideoRenderIosGles20(VideoRenderIosView* view,
23                                           bool full_screen,
24                                           int render_id)
25    : gles_crit_sec_(CriticalSectionWrapper::CreateCriticalSection()),
26      screen_update_event_(0),
27      screen_update_thread_(0),
28      view_(view),
29      window_rect_(),
30      window_width_(0),
31      window_height_(0),
32      is_full_screen_(full_screen),
33      agl_channels_(),
34      z_order_to_channel_(),
35      gles_context_([view context]),
36      is_rendering_(true),
37      id_(render_id) {
38  screen_update_thread_ = ThreadWrapper::CreateThread(
39      ScreenUpdateThreadProc, this, kRealtimePriority);
40  screen_update_event_ = EventWrapper::Create();
41  GetWindowRect(window_rect_);
42}
43
44VideoRenderIosGles20::~VideoRenderIosGles20() {
45  // Signal event to exit thread, then delete it
46  ThreadWrapper* thread_wrapper = screen_update_thread_;
47  screen_update_thread_ = NULL;
48
49  if (thread_wrapper) {
50    thread_wrapper->SetNotAlive();
51    screen_update_event_->Set();
52    screen_update_event_->StopTimer();
53
54    if (thread_wrapper->Stop()) {
55      delete thread_wrapper;
56    }
57    delete screen_update_event_;
58    screen_update_event_ = NULL;
59    is_rendering_ = FALSE;
60  }
61
62  // Delete all channels
63  std::map<int, VideoRenderIosChannel*>::iterator it = agl_channels_.begin();
64  while (it != agl_channels_.end()) {
65    delete it->second;
66    agl_channels_.erase(it);
67    it = agl_channels_.begin();
68  }
69  agl_channels_.clear();
70
71  // Clean the zOrder map
72  std::multimap<int, int>::iterator z_it = z_order_to_channel_.begin();
73  while (z_it != z_order_to_channel_.end()) {
74    z_order_to_channel_.erase(z_it);
75    z_it = z_order_to_channel_.begin();
76  }
77  z_order_to_channel_.clear();
78}
79
80int VideoRenderIosGles20::Init() {
81  CriticalSectionScoped cs(gles_crit_sec_.get());
82
83  if (!screen_update_thread_) {
84    return -1;
85  }
86
87  if (!view_) {
88    view_ = [[VideoRenderIosView alloc] init];
89  }
90
91  if (![view_ createContext]) {
92    return -1;
93  }
94
95  unsigned int thread_id;
96  screen_update_thread_->Start(thread_id);
97
98  // Start the event triggering the render process
99  unsigned int monitor_freq = 60;
100  screen_update_event_->StartTimer(true, 1000 / monitor_freq);
101
102  window_width_ = window_rect_.right - window_rect_.left;
103  window_height_ = window_rect_.bottom - window_rect_.top;
104
105  return 0;
106}
107
108VideoRenderIosChannel* VideoRenderIosGles20::CreateEaglChannel(int channel,
109                                                               int z_order,
110                                                               float left,
111                                                               float top,
112                                                               float right,
113                                                               float bottom) {
114  CriticalSectionScoped cs(gles_crit_sec_.get());
115
116  if (HasChannel(channel)) {
117    return NULL;
118  }
119
120  VideoRenderIosChannel* new_eagl_channel = new VideoRenderIosChannel(view_);
121
122  if (new_eagl_channel->SetStreamSettings(z_order, left, top, right, bottom) ==
123      -1) {
124    return NULL;
125  }
126
127  agl_channels_[channel] = new_eagl_channel;
128  z_order_to_channel_.insert(std::pair<int, int>(z_order, channel));
129
130  return new_eagl_channel;
131}
132
133int VideoRenderIosGles20::DeleteEaglChannel(int channel) {
134  CriticalSectionScoped cs(gles_crit_sec_.get());
135
136  std::map<int, VideoRenderIosChannel*>::iterator it;
137  it = agl_channels_.find(channel);
138  if (it != agl_channels_.end()) {
139    delete it->second;
140    agl_channels_.erase(it);
141  } else {
142    return -1;
143  }
144
145  std::multimap<int, int>::iterator z_it = z_order_to_channel_.begin();
146  while (z_it != z_order_to_channel_.end()) {
147    if (z_it->second == channel) {
148      z_order_to_channel_.erase(z_it);
149      break;
150    }
151    z_it++;
152  }
153
154  return 0;
155}
156
157bool VideoRenderIosGles20::HasChannel(int channel) {
158  CriticalSectionScoped cs(gles_crit_sec_.get());
159
160  std::map<int, VideoRenderIosChannel*>::iterator it =
161      agl_channels_.find(channel);
162
163  if (it != agl_channels_.end()) {
164    return true;
165  }
166
167  return false;
168}
169
170// Rendering process
171bool VideoRenderIosGles20::ScreenUpdateThreadProc(void* obj) {
172  return static_cast<VideoRenderIosGles20*>(obj)->ScreenUpdateProcess();
173}
174
175bool VideoRenderIosGles20::ScreenUpdateProcess() {
176  screen_update_event_->Wait(100);
177
178  CriticalSectionScoped cs(gles_crit_sec_.get());
179
180  if (!is_rendering_) {
181    return false;
182  }
183
184  if (!screen_update_thread_) {
185    return false;
186  }
187
188  if (GetWindowRect(window_rect_) == -1) {
189    return true;
190  }
191
192  if (window_width_ != (window_rect_.right - window_rect_.left) ||
193      window_height_ != (window_rect_.bottom - window_rect_.top)) {
194    window_width_ = window_rect_.right - window_rect_.left;
195    window_height_ = window_rect_.bottom - window_rect_.top;
196  }
197
198  // Check if there are any updated buffers
199  bool updated = false;
200
201  std::map<int, VideoRenderIosChannel*>::iterator it = agl_channels_.begin();
202  while (it != agl_channels_.end()) {
203    VideoRenderIosChannel* agl_channel = it->second;
204
205    updated = agl_channel->IsUpdated();
206    if (updated) {
207      break;
208    }
209    it++;
210  }
211
212  if (updated) {
213    // At least one buffer has been updated, we need to repaint the texture
214    // Loop through all channels starting highest zOrder ending with lowest.
215    for (std::multimap<int, int>::reverse_iterator r_it =
216             z_order_to_channel_.rbegin();
217         r_it != z_order_to_channel_.rend();
218         r_it++) {
219      int channel_id = r_it->second;
220      std::map<int, VideoRenderIosChannel*>::iterator it =
221          agl_channels_.find(channel_id);
222
223      VideoRenderIosChannel* agl_channel = it->second;
224
225      agl_channel->RenderOffScreenBuffer();
226    }
227
228    [view_ presentFramebuffer];
229  }
230
231  return true;
232}
233
234int VideoRenderIosGles20::GetWindowRect(Rect& rect) {
235  CriticalSectionScoped cs(gles_crit_sec_.get());
236
237  if (!view_) {
238    return -1;
239  }
240
241  CGRect bounds = [view_ bounds];
242  rect.top = bounds.origin.y;
243  rect.left = bounds.origin.x;
244  rect.bottom = bounds.size.height + bounds.origin.y;
245  rect.right = bounds.size.width + bounds.origin.x;
246
247  return 0;
248}
249
250int VideoRenderIosGles20::ChangeWindow(void* new_window) {
251  CriticalSectionScoped cs(gles_crit_sec_.get());
252
253  view_ = (__bridge VideoRenderIosView*)new_window;
254
255  return 0;
256}
257
258int VideoRenderIosGles20::ChangeUniqueID(int unique_id) {
259  CriticalSectionScoped cs(gles_crit_sec_.get());
260
261  id_ = unique_id;
262
263  return 0;
264}
265
266int VideoRenderIosGles20::StartRender() {
267  is_rendering_ = true;
268  return 0;
269}
270
271int VideoRenderIosGles20::StopRender() {
272  is_rendering_ = false;
273  return 0;
274}
275
276int VideoRenderIosGles20::GetScreenResolution(uint& screen_width,
277                                              uint& screen_height) {
278  screen_width = [view_ bounds].size.width;
279  screen_height = [view_ bounds].size.height;
280  return 0;
281}
282
283int VideoRenderIosGles20::SetStreamCropping(const uint stream_id,
284                                            const float left,
285                                            const float top,
286                                            const float right,
287                                            const float bottom) {
288  // Check if there are any updated buffers
289  // bool updated = false;
290  uint counter = 0;
291
292  std::map<int, VideoRenderIosChannel*>::iterator it = agl_channels_.begin();
293  while (it != agl_channels_.end()) {
294    if (counter == stream_id) {
295      VideoRenderIosChannel* agl_channel = it->second;
296      agl_channel->SetStreamSettings(0, left, top, right, bottom);
297    }
298    counter++;
299    it++;
300  }
301
302  return 0;
303}
304