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