Декодирование видео с помощью MetalKit или MTKView - PullRequest
0 голосов
/ 20 апреля 2020

Я пытаюсь отобразить видеослой в своем приложении, используя MetalKit framework. К сожалению, я столкнулся с проблемой декодирования видео.

enter image description here

Как показано на рисунке, черный цвет - мой MTKView. Итак, вот моя проблема: контент, помеченный красным цветом, - это мой видеоконтент, но теперь он показывает четыре части. А также видео цветное, но здесь, в моем коде, оно показывает черно-белое. Как я могу исправить эти проблемы.

Вот мой фрагмент кода:

    // Metal kit view object
    metalView = MTKView(frame: view.bounds, device: device)
    metalView.delegate = self
    metalView.framebufferOnly = false
    metalView.colorPixelFormat = .bgra8Unorm
    metalView.contentScaleFactor = UIScreen.main.scale
    metalView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    view.insertSubview(metalView, at: 0)

    // Render pipeline descriptor
    let pipelineDescriptor = MTLRenderPipelineDescriptor()
    pipelineDescriptor.label = self.pipelineLabel
    pipelineDescriptor.vertexFunction =  library.makeFunction(name: self.vertexShaderFunctionName)
    pipelineDescriptor.fragmentFunction = library.makeFunction(name: self.fragmentShaderFunctionName)
    print("pipelineDescriptor.colorAttachments \(pipelineDescriptor.colorAttachments)")
    pipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
    do {
        self.pipelineState = try device?.makeRenderPipelineState(descriptor: pipelineDescriptor)
    } catch {
        print("Failed to make render pipeline state: \(error.localizedDescription)")
    }
    let vertices: [VertexData] = [
        VertexData(pos: simd_float4(x: -1, y: -1, z: 0, w: 1),
                   texCoords: simd_float2(x: 0, y: 1)),
        VertexData(pos: simd_float4(x: 1, y: -1, z: 0, w: 1),
                   texCoords: simd_float2(x: 1, y: 1)),
        VertexData(pos: simd_float4(x: -1, y: 1, z: 0, w: 1),
                   texCoords: simd_float2(x: 0, y: 0)),
        VertexData(pos: simd_float4(x: 1, y: 1, z: 0, w: 1),
                   texCoords: simd_float2(x: 1, y: 0)),
        ]
    self.verticesBuffer = device?.makeBuffer(
        bytes: vertices,
        length: MemoryLayout<VertexData>.stride * vertices.count,
        options: [])

Это MTKViewDelegate реализация

extension MTKViewController: MTKViewDelegate {
    public func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
        NSLog("MTKView drawable size will change to \(size)")
        self.mDrawableSize = size
    }

    public func draw(in: MTKView) {
        _ = semaphore.wait(timeout: DispatchTime.distantFuture)
        commandQueue = device?.makeCommandQueue()
        autoreleasepool {
            guard
                var texture = texture,
                let device = device,
                let commandBuffer = commandQueue?.makeCommandBuffer()
            else {
                _ = semaphore.signal()
                return
            }

            willRenderTexture(&texture, withCommandBuffer: commandBuffer, device: device)
            render(texture: texture, withCommandBuffer: commandBuffer, device: device)
        }
    }

    /**
     Renders texture into the `UIViewController`'s view.

     - parameter texture:       Texture to be rendered
     - parameter commandBuffer: Command buffer we will use for drawing
     */
    private func render(texture: MTLTexture, withCommandBuffer commandBuffer: MTLCommandBuffer, device: MTLDevice) {
        guard
            let currentRenderPassDescriptor = metalView.currentRenderPassDescriptor,
            let currentDrawable = metalView.currentDrawable,
            let renderCommandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: currentRenderPassDescriptor)
        else {
            print("Failed to encoding...")
            semaphore.signal()
            return
        }

        renderCommandEncoder.setViewport(MTLViewport(
            originX: 0.0, originY: 0.0,
            width: Double(self.mDrawableSize.width), height: Double(self.mDrawableSize.height),
            znear: -1.0, zfar: 1.0))

        do {
            var texCoordsScales = simd_float2(x: 1, y: 1)
            var scaleFactor = self.mDrawableSize.width / CGFloat(texture.width)
            let textureFitHeight = CGFloat(texture.height) * scaleFactor
            if textureFitHeight > self.mDrawableSize.height {
                scaleFactor = self.mDrawableSize.height / CGFloat(self.mDrawableSize.height)
                let textureFitWidth = CGFloat(texture.width) * scaleFactor
                let texCoordsScaleX = textureFitWidth / self.mDrawableSize.width
                texCoordsScales.x = Float(texCoordsScaleX)
            } else {
                let texCoordsScaleY = textureFitHeight / self.mDrawableSize.height
                texCoordsScales.y = Float(texCoordsScaleY)
            }

            renderCommandEncoder.setFragmentBytes(&texCoordsScales,
                                                  length: MemoryLayout<simd_float2>.stride,
                                                  index: 0)

            renderCommandEncoder.setFragmentTexture(texture, index: 0)
        }

        self.encodeDefaultCommands(using: renderCommandEncoder)

        renderCommandEncoder.endEncoding()
        print("Encoding...")
        commandBuffer.addScheduledHandler { [weak self] (buffer) in
            guard let unwrappedSelf = self else { return }

            unwrappedSelf.didRenderTexture(texture, withCommandBuffer: buffer, device: device)
            unwrappedSelf.semaphore.signal()
        }
        commandBuffer.present(currentDrawable)
        commandBuffer.commit()
    }

    func encodeDefaultCommands(using encoder: MTLRenderCommandEncoder) {
        guard let lPipelineState = self.pipelineState,
            let lVerticesBuffer = self.verticesBuffer else {
                print("failed to get renderer pipelineState & verticesBuffer.")
                return
        }
        encoder.setRenderPipelineState(lPipelineState)
        encoder.setVertexBuffer(lVerticesBuffer, offset: 0, index: 0)
        encoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4, instanceCount: 1)
    }}

Функции шейдера

typedef struct {
    float4 pos [[position]];
    float2 texCoords;
} RasterizerData;


typedef struct {
    float4 pos;
    float2 texCoords;
} VertexData;

vertex RasterizerData
defaultVertexShader(uint vertexID [[vertex_id]],
                    constant VertexData *vertices [[buffer(0)]]) {
    RasterizerData out;

    out.pos = vector_float4(0.0, 0.0, 0.0, 1.0);
    out.pos.xy = vertices[vertexID].pos.xy;

    out.texCoords = vertices[vertexID].texCoords;

    return out;
}

fragment float4 defaultFragmentShader(RasterizerData in [[stage_in]], constant float2 &texCoordsScales [[buffer(0)]], texture2d<float> texture [[texture(0)]]) {
    constexpr sampler samplr(filter::linear, mag_filter::linear, min_filter::linear);

    float scaleX = texCoordsScales.x;
    float scaleY = texCoordsScales.y;
    float x = (in.texCoords.x - (1.0 - scaleX) / 2.0) / scaleX;
    float y = (in.texCoords.y - (1.0 - scaleY) / 2.0) / scaleY;
    if (x < 0 || x > 1 || y < 0 || y > 1) {
        return float4(float3(0.0), 1.0);
    }
    float3 color = texture.sample(samplr, float2(x, y)).rgb;
    return float4(color, 1.0);
}

Пожалуйста, предложите мне решение.

...