Я пытаюсь отобразить видеослой в своем приложении, используя MetalKit framework. К сожалению, я столкнулся с проблемой декодирования видео.
Как показано на рисунке, черный цвет - мой 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);
}
Пожалуйста, предложите мне решение.