2017-09-23 15 views
0

iOS 10.3.3이 적용된 iPhone 6S에서 Swift 3을 사용하여 yuv420p 인코딩 비디오를 OpenGL ES2 텍스처로 렌더링하려고합니다.iOS - OpenGL ES 2에서 BiPlanar 픽셀 형식을 사용하여 YUV420p 이미지 렌더링

텍스처 설정 :

var formatType = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange 
    var lumaTexture: CVOpenGLESTexture? 
    var chromaTexture: CVOpenGLESTexture? 
    var mediapixelBuffer: CVPixelBuffer? 
    var ioSurfaceBuffer: CVPixelBuffer? 

    media.videoSamplesBuffer = media.assetReaderOutput?.copyNextSampleBuffer() 
    mediapixelBuffer = CMSampleBufferGetImageBuffer(media.videoSamplesBuffer!)! 
    CVPixelBufferLockBaseAddress(mediapixelBuffer!, .readOnly) 

    let bufferWidth0: Int = CVPixelBufferGetWidthOfPlane(mediapixelBuffer!, 0) 
    let bufferWidth1: Int = CVPixelBufferGetWidthOfPlane(mediapixelBuffer!, 1) 
    let bufferHeight0: Int = CVPixelBufferGetWidthOfPlane(mediapixelBuffer!, 0) 
    let bufferHeight1: Int = CVPixelBufferGetWidthOfPlane(mediapixelBuffer!, 1) 
    let bytesPerRow0: Int = CVPixelBufferGetBytesPerRowOfPlane(mediapixelBuffer!, 0) 
    let bytesPerRow1: Int = CVPixelBufferGetBytesPerRowOfPlane(mediapixelBuffer!, 1) 

    let pixelBufferBaseAddress = CVPixelBufferGetBaseAddress(mediapixelBuffer!) 
    let pixelBufferPlaneAddress0 = CVPixelBufferGetBaseAddressOfPlane(mediapixelBuffer!, 0) 
    let pixelBufferPlaneAddress1 = CVPixelBufferGetBaseAddressOfPlane(mediapixelBuffer!, 1) 

    let ioBufferRet = CVPixelBufferCreate(kCFAllocatorDefault, 
              bufferWidth_, 
              bufferHeight_, 
              self.formatType, 
              attr, 
              &ioSurfaceBuffer) 
    if ioBufferRet != 0 { print("error at `CVPixelBufferCreate`", ioBufferRet) } 

    CVPixelBufferLockBaseAddress(ioSurfaceBuffer!, .readOnly) 

    var copyBufferPlaneAddress0 = CVPixelBufferGetBaseAddressOfPlane(ioSurfaceBuffer!, 0) 
    var copyBufferPlaneAddress1 = CVPixelBufferGetBaseAddressOfPlane(ioSurfaceBuffer!, 1) 

    memcpy(copyBufferPlaneAddress0, pixelBufferPlaneAddress0, bufferHeight0 * bytesPerRow0/2) // Y 
    memcpy(copyBufferPlaneAddress1, pixelBufferPlaneAddress1, bufferHeight1 * bytesPerRow1/2) // UV 

    glActiveTexture(GLenum(GL_TEXTURE0)) 
    if nil != ioSurfaceBuffer && nil != media.vidTexCachePtr { 
     var cvRet = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault, 
                   media.vidTexCachePtr!, 
                   ioSurfaceBuffer!, 
                   nil, 
                   GLenum(GL_TEXTURE_2D), 
                   GLint(GL_RED_EXT), 
                   GLsizei(bufferWidth0), 
                   GLsizei(bufferHeight0), 
                   GLenum(GL_RED_EXT), 
                   GLenum(GL_UNSIGNED_BYTE), 
                   0, 
                   &lumaTexture) 
     if cvRet != 0 { print("0 error at `CVOpenGLESTextureCacheCreateTextureFromImage`", cvRet) } 
    } 
    if nil != lumaTexture { 
     glBindTexture(CVOpenGLESTextureGetTarget(lumaTexture!), CVOpenGLESTextureGetName(lumaTexture!)) 
    } 
    glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MIN_FILTER), GL_LINEAR) 
    glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_S), GL_CLAMP_TO_EDGE) 
    glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_T), GL_CLAMP_TO_EDGE) 

    glActiveTexture(GLenum(GL_TEXTURE1)) 
    if nil != ioSurfaceBuffer && nil != media.vidTexCachePtr { 
     var cvRet = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault, 
                  media.vidTexCachePtr!, 
                  ioSurfaceBuffer!, 
                  nil, 
                  GLenum(GL_TEXTURE_2D), 
                  GLint(GL_RG_EXT), 
                  GLsizei(bufferWidth1), 
                  GLsizei(bufferHeight1), 
                  GLenum(GL_RG_EXT), 
                  GLenum(GL_UNSIGNED_BYTE), 
                  1, 
                  &chromaTexture) 
     if cvRet != 0 { print("1 error at `CVOpenGLESTextureCacheCreateTextureFromImage`", cvRet) } 
    } 
    if nil != chromaTexture { 
     glBindTexture(CVOpenGLESTextureGetTarget(chromaTexture!), CVOpenGLESTextureGetName(chromaTexture!)) 
    } 
    glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MIN_FILTER), GL_LINEAR) 
    glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_S), GL_CLAMP_TO_EDGE) 
    glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_T), GL_CLAMP_TO_EDGE) 

    CVPixelBufferUnlockBaseAddress(mediapixelBuffer!, .readOnly) 
    CVPixelBufferUnlockBaseAddress(ioSurfaceBuffer!, .readOnly) 

조각 셰이더 :

#version 100 
    precision mediump float; 

    varying vec2 vUV; 
    uniform sampler2D SamplerY; 
    uniform sampler2D SamplerUV; 

    void main() { 

     mediump vec3 yuv; 
     lowp vec3 rgb; 

     yuv.x = texture2D(SamplerY, vUV).r; 
     yuv.yz = texture2D(SamplerUV, vUV).rg - vec2(0.5, 0.5); 

     // Using BT.709 which is the standard for HDTV 
     rgb = mat3(  1,  1,  1, 
         0, -.18732, 1.8556, 
        1.57481, -.46813,  0) * yuv; 

     gl_FragColor = vec4(rgb, 1); 
    } 

별도의 휘도 질감 오른쪽 보이지만 별도의 크로마 텍스처 만 CR 채널을 갖고있는 것 같아요. 비디오가 4 : 2 : 0이기 때문에 두 번째 크로마 채널이 비어 있기 때문에 Cb 채널을 "보지"않아야하지만 최종 결과 (컬러 막대 색상이어야 함)는 this처럼 보입니다. 빨간색이 누락되었습니다. (나는 이것이 출력이 BGRA라고 가정한다. 만약 RGBA라면 파란색은 사라진다). 빨간을 어떻게 다시 얻을 수 있습니까?

This post은 내가 경험 한 것과 비슷한 문제를 설명합니다. 그러나이 솔루션은 3 개의 평면 (Y, U 및 V를 따로 따로 사용함)을 사용하고 있지만 두 평면 (Y 및 UV)으로이를 수행하려고합니다. 나는 kCVPixelFormatType_420YpCbCr8Planar 형식을 사용하여 3 개의 평면에 액세스하려고했으나 CVOpenGLESTextureCacheCreateTextureFromImage은 IOSurface를 만들지 못했습니다. 또한 몇 가지 다른 YUV-> RGB 쉐이더 방정식을 시도해 보았고 ffmpeg를 사용하여 CVPixelBuffer를 제공하는 방법을 살펴 보았지만 내 iPhone 구조 (arm64)로 빌드 할 수는 없습니다. 사전에 감사 드리며 어떤 도움이라도 대단히 감사하겠습니다!

답변

0

그래서 SamplerUV 텍스쳐가 실제로 셰이더로 전송되지 않았습니다. (하지만 GPU 캡쳐 프레임에서 볼 수 있었는데 오도 된 것입니다.) SamplerY이 쉐이더에 자동으로 전송 되었기 때문에 두 번째 텍스처 인 SamplerUV도 자동으로 전송 되었기 때문에 (잘못) 가정했습니다. 그래서 이전에 보았던 결과는 Y 텍스처와 UV 텍스처 모두에 사용되는 luma 텍스처였습니다.

문제 고정 누락 라인 :

var SamplerY: GLint = 0 
var SamplerUV: GLint = 1 

SamplerY = glGetUniformLocation(shaderProgram, "SamplerY") 
SamplerUV = glGetUniformLocation(shaderProgram, "SamplerUV") 

glUniform1i(SamplerY, 0) 
glUniform1i(SamplerUV, 1)