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