Металлический вид (MTKView) Проблема с размером чертежа - PullRequest
0 голосов
/ 18 мая 2018

Здесь у меня есть MTKView и запущен простой CIFilter в режиме реального времени на камеру.Это прекрасно работает.

Выпуск

На старых селфи-камерах, таких как iPhone 5, iPad Air, канал выводится на меньшую область. ОБНОВЛЕНИЕ : Обнаружено, что CMSampleBuffer, подаваемый в MTKView, имеет меньший размер, когда это происходит.Я думаю, текстуру в каждом обновлении нужно увеличивать?


 import UIKit
 import MetalPerformanceShaders
 import MetalKit
 import AVFoundation

    final class MetalObject: NSObject, MTKViewDelegate {

        private var metalBufferView            : MTKView?
        private var metalDevice                = MTLCreateSystemDefaultDevice()
        private var metalCommandQueue          : MTLCommandQueue!
        private var metalSourceTexture         : MTLTexture?
        private var context                    : CIContext?
        private var filter                     : CIFilter?


        init(with frame: CGRect, filterType: Int, scaledUp: Bool) {
            super.init()

            self.metalCommandQueue = self.metalDevice!.makeCommandQueue()
            self.metalBufferView = MTKView(frame: frame, device: self.metalDevice)
            self.metalBufferView!.framebufferOnly = false
            self.metalBufferView!.isPaused = true

            self.metalBufferView!.contentScaleFactor = UIScreen.main.nativeScale
            self.metalBufferView!.delegate = self
            self.context = CIContext()


        }


        final func update (sampleBuffer: CMSampleBuffer) {

            var textureCache : CVMetalTextureCache?
            CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, self.metalDevice!,  nil, &textureCache)
            var cameraTexture: CVMetalTexture?

            guard
                let cameraTextureCache = textureCache,
                let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
                    return
            }

            let cameraTextureWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0)
            let cameraTextureHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0)
            CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
                                                      cameraTextureCache,
                                                      pixelBuffer,
                                                      nil,
                                                      MTLPixelFormat.bgra8Unorm,
                                                      cameraTextureWidth,
                                                      cameraTextureHeight,
                                                      0,
                                                      &cameraTexture)

            if let cameraTexture = cameraTexture,
                let metalTexture = CVMetalTextureGetTexture(cameraTexture) {
                self.metalSourceTexture = metalTexture
                self.metalBufferView!.draw()
            }


        }

        //MARK: - Metal View Delegate
        final func draw(in view: MTKView) {

            guard let currentDrawable = self.metalBufferView!.currentDrawable,
                let sourceTexture = self.metalSourceTexture
                else {  return  }

            let commandBuffer = self.metalCommandQueue!.makeCommandBuffer()
            var inputImage = CIImage(mtlTexture: sourceTexture)!.applyingOrientation(self.orientationNumber)

            if self.showFilter {
                self.filter!.setValue(inputImage, forKey: kCIInputImageKey)
                inputImage = filter!.outputImage!
            }


            self.context!.render(inputImage, to: currentDrawable.texture, commandBuffer: commandBuffer, bounds: inputImage.extent, colorSpace: self.colorSpace!)

            commandBuffer.present(currentDrawable)
            commandBuffer.commit()

        }


        final func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {

        }
    }

Наблюдения

  • Это происходит только на селфи-камерах старых устройств
  • Селфи-камеры на более новых устройствах работают нормально, когда возникает проблема, новый контент рисуется в меньшей области (тяготеет к верхнему левому углу), при этом старый контент с задней камеры все еще остается вне нового контента.
  • Ограниченияи размер / расположение Metal View в порядке.
  • self.metalBufferView! .contentScaleFactor = UIScreen.main.nativeScale решает странную проблему масштабирования на устройствах Plus.

1 Ответ

0 голосов
/ 26 мая 2018

Похоже, что разрешение передней (селфи) камеры на старых устройствах ниже, поэтому вам нужно увеличить масштаб видео, если вы хотите, чтобы оно использовало всю ширину или высоту.Поскольку вы уже используете CIContext и Metal, вы можете просто дать команду вызову рендеринга нарисовать изображение в любом понравившемся прямоугольнике.

В вашем методе draw вы выполняете self.context!.render(inputImage, to: currentDrawable.texture, commandBuffer: commandBuffer, bounds: inputImage.extent, colorSpace: self.colorSpace!) - bounds аргумент - это прямоугольник destination , в котором будет отображаться изображение.В настоящее время вы используете экстент изображения, что означает, что изображение не будет масштабироваться.

Чтобы увеличить масштаб видео, используйте вместо этого прямоугольник дисплея.Вы можете просто использовать metalBufferView.bounds, так как это будет размер вашего дисплея.В итоге вы получите self.context!.render(inputImage, to: currentDrawable.texture, commandBuffer: commandBuffer, bounds: self.metalBufferView.bounds, colorSpace: self.colorSpace!)

Если изображение и вид имеют разные соотношения сторон (ширина / высота - это соотношение сторон), то вам нужно будет вычислить правильный размер так, чтобы аспект изображениясоотношение сохраняется.Чтобы сделать это, вы получите код, подобный следующему:

CGRect dest = self.metalBufferView.bounds;
CGSize imageSize = inputImage.extent.size;
CGSize viewSize = dest.size; 
double imageAspect = imageSize.width / imageSize.height;
double viewAspect = viewSize.width / viewSize.height;
if (imageAspect > viewAspect) {
    // the image is wider than the view, adjust height
    dest.size.height = 1/imageAspect * dest.size.width;
} else {
    // the image is taller than the view, adjust the width
    dest.size.width = imageAspect * dest.size.height;

    // center the tall image
    dest.origin.x = (viewSize.width - dest.size.width) / 2;
}

Надеюсь, что это полезно, пожалуйста, дайте мне знать, если что-то не работает, или было бы полезно разъяснение.

...