taontech / githublog

一个基于github issues的博客系统,实时呈现,零依赖,零代码部署,不用打包不用上线。
4 stars 1 forks source link

图片转HDR视频 #13

Open taontech opened 2 years ago

taontech commented 2 years ago

关键代码

重点是这个设置

        let outputSettings = [
            AVVideoCodecKey: AVVideoCodecType.hevc,
            AVVideoWidthKey : NSNumber(value: Float(outputSize.width)),
            AVVideoHeightKey : NSNumber(value: Float(outputSize.height)),
            AVVideoColorPropertiesKey : [ AVVideoTransferFunctionKey: AVVideoTransferFunction_ITU_R_2100_HLG,
                                                        AVVideoColorPrimariesKey: AVVideoColorPrimaries_ITU_R_2020,
                                                        AVVideoYCbCrMatrixKey: AVVideoYCbCrMatrix_ITU_R_2020
                                                         ],
            AVVideoCompressionPropertiesKey:     [AVVideoProfileLevelKey:kVTProfileLevel_HEVC_Main10_AutoLevel,AVVideoAverageBitRateKey:NSNumber(value: Float(3000000))]
        ] as [String:Any]
@IBAction func makeHDRMovie(){

        let hdrimage = hdrImagePixelBuffer(uiImage:(self.imageView?.image)!)

        // todo make movie
        let fileManager = FileManager.default
            let urls = fileManager.urls(for: .cachesDirectory, in: .userDomainMask)
            guard let documentDirectory = urls.first else {
                fatalError("documentDir Error")
            }
        let videoOutputURL = documentDirectory.appendingPathComponent("hdr.mp4")

        if FileManager.default.fileExists(atPath: videoOutputURL.path) {
            do {
                try FileManager.default.removeItem(atPath: videoOutputURL.path)
            } catch {
                fatalError("Unable to delete file: \(error) : \(#function).")
            }
        }
        guard let videoWriter = try? AVAssetWriter(outputURL: videoOutputURL, fileType: AVFileType.mov) else{
            fatalError("AVAssetWriter creat error")
        }
        let outputSize = (self.imageView?.image?.size)!
        let outputSettings = [
            AVVideoCodecKey: AVVideoCodecType.hevc,
            AVVideoWidthKey : NSNumber(value: Float(outputSize.width)),
            AVVideoHeightKey : NSNumber(value: Float(outputSize.height)),
//            AVVideoTransferFunctionKey: AVVideoTransferFunction_ITU_R_2100_HLG,
//            AVVideoColorPrimariesKey: AVVideoColorPrimaries_ITU_R_2020,
//            AVVideoYCbCrMatrixKey: AVVideoYCbCrMatrix_ITU_R_2020,
//            AVVideoProfileLevelKey: kVTProfileLevel_HEVC_Main10_AutoLevel
            AVVideoColorPropertiesKey : [             AVVideoTransferFunctionKey: AVVideoTransferFunction_ITU_R_2100_HLG,
                                                        AVVideoColorPrimariesKey: AVVideoColorPrimaries_ITU_R_2020,
                                                        AVVideoYCbCrMatrixKey: AVVideoYCbCrMatrix_ITU_R_2020
                                                         ],
            AVVideoCompressionPropertiesKey: [AVVideoProfileLevelKey:kVTProfileLevel_HEVC_Main10_AutoLevel,AVVideoAverageBitRateKey:NSNumber(value: Float(3000000))]
        ] as [String:Any]
        guard videoWriter.canApply(outputSettings: outputSettings, forMediaType: .video) else {
            fatalError("输出设定不支持")
        }

//        let av = AVAsset(url: <#T##URL#>)
//        guard let exportSession = AVAssetExportSession(asset: <#T##AVAsset#>, presetName: <#T##String#>)

        let videoWriterInput = AVAssetWriterInput(mediaType: .video, outputSettings: outputSettings)
        let sourcePixelBufferAttributesDictionary = [
            kCVPixelBufferPixelFormatTypeKey as String : NSNumber(value: kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange),
            kCVPixelBufferWidthKey as String: NSNumber(value: Float(outputSize.width)),
            kCVPixelBufferHeightKey as String: NSNumber(value: Float(outputSize.height))
        ]
        pixelBufferAdaptor = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: videoWriterInput, sourcePixelBufferAttributes: sourcePixelBufferAttributesDictionary)
        if videoWriter.canAdd(videoWriterInput){
            videoWriter.add(videoWriterInput)
        }
        if videoWriter.startWriting(){
            videoWriter.startSession(atSourceTime: .zero)
            assert(pixelBufferAdaptor?.pixelBufferPool != nil)
            let madia_queue = DispatchQueue(label: "madiaInputQueue")
            videoWriterInput.requestMediaDataWhenReady(on: madia_queue) { [self] in
                let fps: Int32 = 30
                let frameDuration = CMTimeMake(value: 1, timescale: fps)
                var frameCount: Int64 = 0
                var appendSucceded = true
                // do data things
                // 1st frame
                var lastFrameTime = CMTime(value: frameCount, timescale: fps)
                var presentationTime = frameCount == 0 ? lastFrameTime : CMTimeAdd(lastFrameTime, frameDuration)
                var pixelBuffer: CVPixelBuffer?
//                DispatchQueue.main.sync {
//                    pixelBuffer = buffer(from: (self.imageView?.image!)!)
                    pixelBuffer = buffer(from: hdrimage!)
//                }
                guard let pixelBuffer = pixelBuffer else {
                    return
                }

                while ( frameCount < 90 ){
                    if videoWriterInput.isReadyForMoreMediaData {
                        lastFrameTime = CMTime(value: frameCount, timescale: fps)
                        presentationTime = frameCount == 0 ? lastFrameTime : CMTimeAdd(lastFrameTime, frameDuration)
                        appendSucceded = ((pixelBufferAdaptor?.append(pixelBuffer, withPresentationTime: presentationTime)) != nil)
                    } else {
                        print("append fail")
                        appendSucceded = false
//                        sleep(1)
                        continue
                    }
                    if !appendSucceded{
                        sleep(1)
                       break
                    }
                    print(frameCount)
                    frameCount = frameCount + 1
                }
//                sleep(1)
                videoWriterInput.markAsFinished()
                videoWriter.finishWriting {
                    print("done")
                    self.saveVideoToLibrary(videoURL: videoOutputURL)
                }
            }
        }
    }
    func saveVideoToLibrary(videoURL: URL) {
        PHPhotoLibrary.shared().performChanges({
            PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: videoURL)
        }) { saved, error in

            if let error = error {
                print("Error saving video to librayr: \(error.localizedDescription)")
            }
            if saved {
                print("Video save to library")

            }
        }
    }
    func buffer(from ciimage:CIImage) -> CVPixelBuffer?{
        let attrs = [kCVPixelBufferCGImageCompatibilityKey: kCFBooleanTrue, kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanTrue] as CFDictionary
        var pixelBuffer : CVPixelBuffer?
        let status = CVPixelBufferCreate(kCFAllocatorDefault, Int(ciimage.extent.width), Int(ciimage.extent.height), kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange, attrs, &pixelBuffer)

        guard (status == kCVReturnSuccess) else {
            return nil
        }
        let context = CIContext()
        let cs = context.workingColorSpace
        print(cs)
        context.render(ciimage, to: pixelBuffer!, bounds: ciimage.extent, colorSpace: CGColorSpace(name: CGColorSpace.extendedITUR_2020))

        return pixelBuffer
    }

    func hdrImagePixelBuffer(uiImage:UIImage) -> CIImage?{

        let colorSpace = CGColorSpace(name: CGColorSpace.itur_709)!

        let ci = ciImage!.matchedFromWorkingSpace(to: colorSpace)

        let brightness: CIColorKernel = {
            let url = Bundle.main.url(forResource: "default", withExtension: ".metallib")!
            let data = try! Data.init(contentsOf: url)
            let nn = CIColorKernel.kernelNames(fromMetalLibraryData: data)
    //        CIColorKernel.kernelNames(fromMetalLibraryData: )
            print(nn)
            return try! CIColorKernel(functionName: "HDRHighlight", fromMetalLibraryData: data)
        }()
        let width = uiImage.size.width
        let height = uiImage.size.height
        let extent = CGRect(x: 0, y: 0, width: width, height: height)
        guard let final = brightness.apply(extent: extent,
         roiCallback: {(index,rect) in
               return rect
         },
        arguments: [ci]) else {
            return nil
        }
//        let outcs = CGColorSpace(name: CGColorSpace.displayP3_PQ)
        return final
    }
taontech commented 2 years ago

弱鸡的苹果文档,无力吐槽了