Search code examples
ioscgaffinetransformcifilterciimagecmsamplebuffer

CMSampleBuffer rotate from portrait to landscape in Swift 3


I'm handling ReplayKit2 in iOS, for some reasons I need to rotate CMSampleBuffer from portrait to landscape, I found the result is not correct.

What I miss ?

this is original sample buffer enter image description here

this is actual output buffer enter image description here

width & height are dimensions of sampleBuffer

func rotation(sampleBuffer: CMSampleBuffer, width: Int, height: Int) -> CMSampleBuffer {

        //create pixelbuffer from the delegate method samplebuffer
        let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!

        CVPixelBufferLockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0))

        //create CI image from the buffer
        let image = CIImage(cvImageBuffer: pixelBuffer)

        let extent = CGRect(x: 0, y: 0, width: width, height: height)

        var tx = CGAffineTransform(translationX: extent.midX, y: extent.midY)

        tx = tx.rotated(by: CGFloat(Double.pi / 2))

        tx = tx.translatedBy(x: -extent.midX, y: -extent.midY)

        var transformImage = CIFilter(
            name: "CIAffineTransform",
            withInputParameters: [
                kCIInputImageKey: image,
                kCIInputTransformKey: NSValue.init(cgAffineTransform: tx)])!.outputImage!

        //create empty pixelbuffer
        var newPixelBuffer : CVPixelBuffer? = nil

        CVPixelBufferCreate(kCFAllocatorDefault,
                            width,
                            height,
                            kCVPixelFormatType_32BGRA,
                            nil,
                            &newPixelBuffer)
        //render the context to the new pixelbuffer, context is a global
        //CIContext variable. creating a new one each frame is too CPU intensive
        self.ciContext.render(transformImage, to: newPixelBuffer!)

        //finally, write this to the pixelbufferadaptor

        CVPixelBufferUnlockBaseAddress(pixelBuffer,CVPixelBufferLockFlags(rawValue: 0))

        var videoInfo: CMVideoFormatDescription?

        CMVideoFormatDescriptionCreateForImageBuffer(kCFAllocatorDefault, newPixelBuffer!, &videoInfo)

        var sampleTimingInfo = CMSampleTimingInfo(duration: CMSampleBufferGetDuration(sampleBuffer), presentationTimeStamp: CMSampleBufferGetPresentationTimeStamp(sampleBuffer), decodeTimeStamp: CMSampleBufferGetDecodeTimeStamp(sampleBuffer))

        var newSampleBuffer: CMSampleBuffer?

        CMSampleBufferCreateForImageBuffer(kCFAllocatorDefault, newPixelBuffer!, true, nil, nil, videoInfo!, &sampleTimingInfo, &newSampleBuffer)

        return newSampleBuffer!

    }

Solution

  • just found a very sweet method in iOS 11!

    /* Returns a new image representing the original image transformeded for the given CGImagePropertyOrientation */
        @available(iOS 11.0, *)
        open func oriented(_ orientation: CGImagePropertyOrientation) -> CIImage