From bea05e367d9ae8175013fa1702ded06993f9e226 Mon Sep 17 00:00:00 2001 From: Blue Date: Tue, 16 Aug 2016 21:52:46 +0800 Subject: [PATCH] Join mix thread before destroy OpenGL env --- mixers/iOS/GLESVideoMixer.mm | 183 ++++++++++++++++++----------------- 1 file changed, 92 insertions(+), 91 deletions(-) diff --git a/mixers/iOS/GLESVideoMixer.mm b/mixers/iOS/GLESVideoMixer.mm index d8c6d641..bc8da016 100644 --- a/mixers/iOS/GLESVideoMixer.mm +++ b/mixers/iOS/GLESVideoMixer.mm @@ -1,18 +1,18 @@ /* - + Video Core Copyright (c) 2014 James G. Hurley - + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -20,7 +20,7 @@ of this software and associated documentation files (the "Software"), to deal LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + */ @@ -70,7 +70,7 @@ - (instancetype) init { if((self = [super init])) { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notification:) name:UIApplicationDidEnterBackgroundNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notification:) name:UIApplicationWillEnterForegroundNotification object:nil]; - + } return self; } @@ -80,13 +80,13 @@ - (void) dealloc { } - (void) notification: (NSNotification*) notification { if([notification.name isEqualToString:UIApplicationDidEnterBackgroundNotification]) { - + _mixer->mixPaused(true); - + } else if([notification.name isEqualToString:UIApplicationWillEnterForegroundNotification]) { - + _mixer->mixPaused(false); - + } } - (void) setMixer: (videocore::iOS::GLESVideoMixer*) mixer @@ -95,7 +95,7 @@ - (void) setMixer: (videocore::iOS::GLESVideoMixer*) mixer } @end namespace videocore { namespace iOS { - + // ------------------------------------------------------------------------- // // SourceBuffer::setBuffer @@ -103,29 +103,29 @@ - (void) setMixer: (videocore::iOS::GLESVideoMixer*) mixer // texture. Textures unused for more than 1 second will be released. // // ------------------------------------------------------------------------- - + void SourceBuffer::setBuffer(Apple::PixelBufferRef ref, CVOpenGLESTextureCacheRef textureCache, JobQueue& m_glJobQueue, void* m_glesCtx) { - + bool flush = false; auto it = m_pixelBuffers.find(ref->cvBuffer()); const auto now = std::chrono::steady_clock::now(); - + if(m_currentBuffer) { m_currentBuffer->setState(kVCPixelBufferStateAvailable); } ref->setState(kVCPixelBufferStateAcquired); - + if(it == m_pixelBuffers.end()) { PERF_GL_async({ - + ref->lock(true); OSType format = (OSType)ref->pixelFormat(); bool is32bit = true; - + is32bit = (format != kCVPixelFormatType_16LE565); - + CVOpenGLESTextureRef texture = nullptr; CVReturn ret = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault, textureCache, @@ -139,7 +139,7 @@ - (void) setMixer: (videocore::iOS::GLESVideoMixer*) mixer is32bit ? GL_UNSIGNED_BYTE : GL_UNSIGNED_SHORT_5_6_5, 0, &texture); - + ref->unlock(true); if(ret == noErr && texture) { glBindTexture(GL_TEXTURE_2D, CVOpenGLESTextureGetName(texture)); @@ -147,14 +147,14 @@ - (void) setMixer: (videocore::iOS::GLESVideoMixer*) mixer glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - + auto iit = this->m_pixelBuffers.emplace(ref->cvBuffer(), ref).first; iit->second.texture = texture; - + this->m_currentBuffer = ref; this->m_currentTexture = texture; iit->second.time = now; - + } else { DLog("%d: Error creating texture! (%ld)", __LINE__, (long)ret); } @@ -165,25 +165,25 @@ - (void) setMixer: (videocore::iOS::GLESVideoMixer*) mixer m_currentBuffer = ref; m_currentTexture = it->second.texture; it->second.time = now; - + } - + PERF_GL_async({ //const auto currentBuffer = this->m_currentBuffer->cvBuffer(); for ( auto it = this->m_pixelBuffers.begin() ; it != m_pixelBuffers.end() ; ) { - + if ( (it->second.buffer->isTemporary()) && it->second.buffer->cvBuffer() != this->m_currentBuffer->cvBuffer() ) { // Buffer is temporary, release it. it = this->m_pixelBuffers.erase(it); } else { ++ it; } - + } if(flush) { CVOpenGLESTextureCacheFlush(textureCache, 0); } - + }); } // ------------------------------------------------------------------------- @@ -210,23 +210,27 @@ - (void) setMixer: (videocore::iOS::GLESVideoMixer*) mixer m_epoch(std::chrono::steady_clock::now()) { PERF_GL_sync({ - + this->setupGLES(excludeContext); - + }); m_zRange.first = INT_MAX; m_zRange.second = INT_MIN; - + m_callbackSession = [[GLESObjCCallback alloc] init]; [(GLESObjCCallback*)m_callbackSession setMixer:this]; - + } - + GLESVideoMixer::~GLESVideoMixer() { m_output.reset(); m_exiting = true; m_mixThreadCond.notify_all(); + if(m_mixThread.joinable()) { + m_mixThread.join(); + } + DLog("GLESVideoMixer::~GLESVideoMixer()"); PERF_GL_sync({ //glDeleteProgram(m_prog); @@ -237,26 +241,23 @@ - (void) setMixer: (videocore::iOS::GLESVideoMixer*) mixer textures[0] = CVOpenGLESTextureGetName(m_texture[0]); textures[1] = CVOpenGLESTextureGetName(m_texture[1]); glDeleteTextures(2, textures); - + m_sourceBuffers.clear(); - + CVPixelBufferRelease(m_pixelBuffer[0]); CVPixelBufferRelease(m_pixelBuffer[1]); CFRelease(m_texture[0]); CFRelease(m_texture[1]); CVOpenGLESTextureCacheFlush(m_textureCache, 0); CFRelease(m_textureCache); - + [(id)m_glesCtx release]; }); - - if(m_mixThread.joinable()) { - m_mixThread.join(); - } + m_glJobQueue.mark_exiting(); m_glJobQueue.enqueue_sync([](){}); - + [(id)m_callbackSession release]; } void @@ -281,7 +282,7 @@ - (void) setMixer: (videocore::iOS::GLESVideoMixer*) mixer if(excludeContext) { excludeContext(m_glesCtx); } - + // // Shared-memory FBOs // @@ -296,16 +297,16 @@ - (void) setMixer: (videocore::iOS::GLESVideoMixer*) mixer // 4. Create an OpenGL ES Framebuffer for each CVOpenGLESTextureRef and set that texture as the color attachment. // // We may now attach these FBOs as the render target and avoid using the costly glGetPixels. - + @autoreleasepool { - + if(!m_pixelBufferPool) { NSDictionary* pixelBufferOptions = @{ (NSString*) kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA), (NSString*) kCVPixelBufferWidthKey : @(m_frameW), (NSString*) kCVPixelBufferHeightKey : @(m_frameH), (NSString*) kCVPixelBufferOpenGLESCompatibilityKey : @YES, (NSString*) kCVPixelBufferIOSurfacePropertiesKey : @{}}; - + CVPixelBufferCreate(kCFAllocatorDefault, m_frameW, m_frameH, kCVPixelFormatType_32BGRA, (CFDictionaryRef)pixelBufferOptions, &m_pixelBuffer[0]); CVPixelBufferCreate(kCFAllocatorDefault, m_frameW, m_frameH, kCVPixelFormatType_32BGRA, (CFDictionaryRef)pixelBufferOptions, &m_pixelBuffer[1]); } @@ -313,13 +314,13 @@ - (void) setMixer: (videocore::iOS::GLESVideoMixer*) mixer CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, m_pixelBufferPool, &m_pixelBuffer[0]); CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, m_pixelBufferPool, &m_pixelBuffer[1]); } - + } CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, (EAGLContext*)this->m_glesCtx, NULL, &this->m_textureCache); glGenFramebuffers(2, this->m_fbo); for(int i = 0 ; i < 2 ; ++i) { CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault, this->m_textureCache, this->m_pixelBuffer[i], NULL, GL_TEXTURE_2D, GL_RGBA, m_frameW, m_frameH, GL_BGRA, GL_UNSIGNED_BYTE, 0, &m_texture[i]); - + glBindTexture(GL_TEXTURE_2D, CVOpenGLESTextureGetName(m_texture[i])); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); @@ -327,16 +328,16 @@ - (void) setMixer: (videocore::iOS::GLESVideoMixer*) mixer glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glBindFramebuffer(GL_FRAMEBUFFER, m_fbo[i]); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, CVOpenGLESTextureGetName(m_texture[i]), 0); - + } - - + + GL_FRAMEBUFFER_STATUS(__LINE__); - + glGenBuffers(1, &this->m_vbo); glBindBuffer(GL_ARRAY_BUFFER, this->m_vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(s_vbo), s_vbo, GL_STATIC_DRAW); - + glDisable(GL_BLEND); glDisable(GL_DEPTH_TEST); glDisable(GL_SCISSOR_TEST); @@ -350,7 +351,7 @@ - (void) setMixer: (videocore::iOS::GLESVideoMixer*) mixer { const auto hash = std::hash< std::shared_ptr > () (source); bool registered = false; - + for ( auto it : m_sources) { auto lsource = it.lock(); if(lsource) { @@ -375,25 +376,25 @@ - (void) setMixer: (videocore::iOS::GLESVideoMixer*) mixer if(it != m_sourceBuffers.end()) { m_sourceBuffers.erase(it); } - + } void GLESVideoMixer::unregisterSource(std::shared_ptr source) { DLog("GLESVideoMixer::unregisterSource"); releaseBuffer(source); - + auto it = m_sources.begin(); const auto h = std::hash >()(source); for ( ; it != m_sources.end() ; ++it ) { - + const auto shash = hash(*it); - + if(h == shash) { m_sources.erase(it); break; } - + } { auto iit = m_sourceBuffers.find(h); @@ -410,7 +411,7 @@ - (void) setMixer: (videocore::iOS::GLESVideoMixer*) mixer } } } - + } void GLESVideoMixer::pushBuffer(const uint8_t *const data, @@ -420,29 +421,29 @@ - (void) setMixer: (videocore::iOS::GLESVideoMixer*) mixer if(m_paused.load()) { return; } - + VideoBufferMetadata & md = dynamic_cast(metadata); const int zIndex = md.getData(); - + const glm::mat4 mat = md.getData(); - + if(zIndex < m_zRange.first) { m_zRange.first = zIndex; } if(zIndex > m_zRange.second){ m_zRange.second = zIndex; } - + std::weak_ptr source = md.getData(); - + const auto h = hash(source); - - + + auto inPixelBuffer = *(Apple::PixelBufferRef*)data ; m_sourceBuffers[h].setBuffer(inPixelBuffer, this->m_textureCache, m_glJobQueue, m_glesCtx); m_sourceBuffers[h].setBlends(md.getData()); - + auto it = std::find(this->m_layerMap[zIndex].begin(), this->m_layerMap[zIndex].end(), h); if(it == this->m_layerMap[zIndex].end()) { this->m_layerMap[zIndex].push_back(h); @@ -469,45 +470,45 @@ - (void) setMixer: (videocore::iOS::GLESVideoMixer*) mixer const auto us = std::chrono::microseconds(static_cast(m_bufferDuration * 1000000.)); const auto us_25 = std::chrono::microseconds(static_cast(m_bufferDuration * 250000.)); m_us25 = us_25; - + pthread_setname_np("com.videocore.compositeloop"); - + int current_fb = 0; - + bool locked[2] = {false}; - + m_nextMixTime = m_epoch; - + while(!m_exiting.load()) { std::unique_lock l(m_mutex); const auto now = std::chrono::steady_clock::now(); - + if(now >= (m_nextMixTime)) { - + auto currentTime = m_nextMixTime; if(!m_shouldSync) { m_nextMixTime += us; } else { m_nextMixTime = m_syncPoint > m_nextMixTime ? m_syncPoint + us : m_nextMixTime + us; } - - + + if(m_mixing.load() || m_paused.load()) { continue; } - + locked[current_fb] = true; - + m_mixing = true; PERF_GL_async({ glPushGroupMarkerEXT(0, "Videocore.Mix"); glBindFramebuffer(GL_FRAMEBUFFER, this->m_fbo[current_fb]); - + IVideoFilter* currentFilter = nil; glClear(GL_COLOR_BUFFER_BIT); for ( int i = m_zRange.first ; i <= m_zRange.second ; ++i) { - + for ( auto it = this->m_layerMap[i].begin() ; it != this->m_layerMap[i].end() ; ++ it) { CVOpenGLESTextureRef texture = NULL; auto filterit = m_sourceFilters.find(*it); @@ -520,17 +521,17 @@ - (void) setMixer: (videocore::iOS::GLESVideoMixer*) mixer currentFilter->unbind(); } currentFilter = m_sourceFilters[*it]; - + if(currentFilter && !currentFilter->initialized()) { currentFilter->initialize(); } } - + auto iTex = this->m_sourceBuffers.find(*it); if(iTex == this->m_sourceBuffers.end()) continue; - + texture = iTex->second.currentTexture(); - + // TODO: Add blending. if(iTex->second.blends()) { glEnable(GL_BLEND); @@ -551,31 +552,31 @@ - (void) setMixer: (videocore::iOS::GLESVideoMixer*) mixer } glFlush(); glPopGroupMarkerEXT(); - - + + auto lout = this->m_output.lock(); if(lout) { - + MetaData<'vide'> md(std::chrono::duration_cast(currentTime - m_epoch).count()); lout->pushBuffer((uint8_t*)this->m_pixelBuffer[!current_fb], sizeof(this->m_pixelBuffer[!current_fb]), md); } this->m_mixing = false; - + }); current_fb = !current_fb; } - + m_mixThreadCond.wait_until(l, m_nextMixTime); - + } } - + void GLESVideoMixer::mixPaused(bool paused) { m_paused = paused; } - + void GLESVideoMixer::setSourceFilter(std::weak_ptr source, IVideoFilter *filter) { auto h = hash(source);