/* * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #if !defined(__has_feature) || !__has_feature(objc_arc) #error "This file requires ARC support." #endif #include "webrtc/modules/video_render/ios/video_render_ios_gles20.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/event_wrapper.h" #include "webrtc/system_wrappers/interface/thread_wrapper.h" using namespace webrtc; VideoRenderIosGles20::VideoRenderIosGles20(VideoRenderIosView* view, bool full_screen, int render_id) : gles_crit_sec_(CriticalSectionWrapper::CreateCriticalSection()), screen_update_event_(0), screen_update_thread_(0), view_(view), window_rect_(), window_width_(0), window_height_(0), is_full_screen_(full_screen), agl_channels_(), z_order_to_channel_(), gles_context_([view context]), is_rendering_(true), id_(render_id) { screen_update_thread_ = ThreadWrapper::CreateThread( ScreenUpdateThreadProc, this, kRealtimePriority); screen_update_event_ = EventWrapper::Create(); GetWindowRect(window_rect_); } VideoRenderIosGles20::~VideoRenderIosGles20() { // Signal event to exit thread, then delete it ThreadWrapper* thread_wrapper = screen_update_thread_; screen_update_thread_ = NULL; if (thread_wrapper) { thread_wrapper->SetNotAlive(); screen_update_event_->Set(); screen_update_event_->StopTimer(); if (thread_wrapper->Stop()) { delete thread_wrapper; } delete screen_update_event_; screen_update_event_ = NULL; is_rendering_ = FALSE; } // Delete all channels std::map::iterator it = agl_channels_.begin(); while (it != agl_channels_.end()) { delete it->second; agl_channels_.erase(it); it = agl_channels_.begin(); } agl_channels_.clear(); // Clean the zOrder map std::multimap::iterator z_it = z_order_to_channel_.begin(); while (z_it != z_order_to_channel_.end()) { z_order_to_channel_.erase(z_it); z_it = z_order_to_channel_.begin(); } z_order_to_channel_.clear(); } int VideoRenderIosGles20::Init() { CriticalSectionScoped cs(gles_crit_sec_.get()); if (!screen_update_thread_) { return -1; } if (!view_) { view_ = [[VideoRenderIosView alloc] init]; } if (![view_ createContext]) { return -1; } unsigned int thread_id; screen_update_thread_->Start(thread_id); // Start the event triggering the render process unsigned int monitor_freq = 60; screen_update_event_->StartTimer(true, 1000 / monitor_freq); window_width_ = window_rect_.right - window_rect_.left; window_height_ = window_rect_.bottom - window_rect_.top; return 0; } VideoRenderIosChannel* VideoRenderIosGles20::CreateEaglChannel(int channel, int z_order, float left, float top, float right, float bottom) { CriticalSectionScoped cs(gles_crit_sec_.get()); if (HasChannel(channel)) { return NULL; } VideoRenderIosChannel* new_eagl_channel = new VideoRenderIosChannel(view_); if (new_eagl_channel->SetStreamSettings(z_order, left, top, right, bottom) == -1) { return NULL; } agl_channels_[channel] = new_eagl_channel; z_order_to_channel_.insert(std::pair(z_order, channel)); return new_eagl_channel; } int VideoRenderIosGles20::DeleteEaglChannel(int channel) { CriticalSectionScoped cs(gles_crit_sec_.get()); std::map::iterator it; it = agl_channels_.find(channel); if (it != agl_channels_.end()) { delete it->second; agl_channels_.erase(it); } else { return -1; } std::multimap::iterator z_it = z_order_to_channel_.begin(); while (z_it != z_order_to_channel_.end()) { if (z_it->second == channel) { z_order_to_channel_.erase(z_it); break; } z_it++; } return 0; } bool VideoRenderIosGles20::HasChannel(int channel) { CriticalSectionScoped cs(gles_crit_sec_.get()); std::map::iterator it = agl_channels_.find(channel); if (it != agl_channels_.end()) { return true; } return false; } // Rendering process bool VideoRenderIosGles20::ScreenUpdateThreadProc(void* obj) { return static_cast(obj)->ScreenUpdateProcess(); } bool VideoRenderIosGles20::ScreenUpdateProcess() { screen_update_event_->Wait(100); CriticalSectionScoped cs(gles_crit_sec_.get()); if (!is_rendering_) { return false; } if (!screen_update_thread_) { return false; } if (GetWindowRect(window_rect_) == -1) { return true; } if (window_width_ != (window_rect_.right - window_rect_.left) || window_height_ != (window_rect_.bottom - window_rect_.top)) { window_width_ = window_rect_.right - window_rect_.left; window_height_ = window_rect_.bottom - window_rect_.top; } // Check if there are any updated buffers bool updated = false; std::map::iterator it = agl_channels_.begin(); while (it != agl_channels_.end()) { VideoRenderIosChannel* agl_channel = it->second; updated = agl_channel->IsUpdated(); if (updated) { break; } it++; } if (updated) { // At least one buffer has been updated, we need to repaint the texture // Loop through all channels starting highest zOrder ending with lowest. for (std::multimap::reverse_iterator r_it = z_order_to_channel_.rbegin(); r_it != z_order_to_channel_.rend(); r_it++) { int channel_id = r_it->second; std::map::iterator it = agl_channels_.find(channel_id); VideoRenderIosChannel* agl_channel = it->second; agl_channel->RenderOffScreenBuffer(); } [view_ presentFramebuffer]; } return true; } int VideoRenderIosGles20::GetWindowRect(Rect& rect) { CriticalSectionScoped cs(gles_crit_sec_.get()); if (!view_) { return -1; } CGRect bounds = [view_ bounds]; rect.top = bounds.origin.y; rect.left = bounds.origin.x; rect.bottom = bounds.size.height + bounds.origin.y; rect.right = bounds.size.width + bounds.origin.x; return 0; } int VideoRenderIosGles20::ChangeWindow(void* new_window) { CriticalSectionScoped cs(gles_crit_sec_.get()); view_ = (__bridge VideoRenderIosView*)new_window; return 0; } int VideoRenderIosGles20::ChangeUniqueID(int unique_id) { CriticalSectionScoped cs(gles_crit_sec_.get()); id_ = unique_id; return 0; } int VideoRenderIosGles20::StartRender() { is_rendering_ = true; return 0; } int VideoRenderIosGles20::StopRender() { is_rendering_ = false; return 0; } int VideoRenderIosGles20::GetScreenResolution(uint& screen_width, uint& screen_height) { screen_width = [view_ bounds].size.width; screen_height = [view_ bounds].size.height; return 0; } int VideoRenderIosGles20::SetStreamCropping(const uint stream_id, const float left, const float top, const float right, const float bottom) { // Check if there are any updated buffers // bool updated = false; uint counter = 0; std::map::iterator it = agl_channels_.begin(); while (it != agl_channels_.end()) { if (counter == stream_id) { VideoRenderIosChannel* agl_channel = it->second; agl_channel->SetStreamSettings(0, left, top, right, bottom); } counter++; it++; } return 0; }