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