You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS:从分享扩展向应用传输视频时画质降低问题求助

iOS分享扩展传输视频画质降低问题排查与解决

从iOS分享扩展向主应用传输视频时,视频画质会出现降低,但直接通过主应用选择并上传视频时无此问题。相关加载视频文件的代码如下:

func handleSharedFile() async {
    loader.startAnimating()
    let attachments = (self.extensionContext?.inputItems.first as? NSExtensionItem)?.attachments ?? []
    let contentTypes = [UTType.data, .url]
    for provider in attachments {
        for contentType in contentTypes {
            guard let loadedItem = await provider.load(type: contentType) else { continue }
            if let url = loadedItem as? URL {
                await MainActor.run {
                    if let uti = UTType(tag: url.pathExtension, tagClass: .filenameExtension, conformingTo: nil) {
                        let videoTypes = [UTType.movie, .video, .mpeg4Movie, .movie, .quickTimeMovie]
                        if videoTypes.contains(uti) {
                            if UIVideoEditorController.canEditVideo(atPath: url.path)  {
                                let editorController = UIVideoEditorController()
                                editorController.videoMaximumDuration = 90
                                editorController.videoPath = url.path
                                editorController.delegate = self
                                present(editorController, animated: true)
                            } else {
                                Task {
                                    let convertedUrl = await VideoConverterHelper().convertVideo(at: url, isShare: true)
                                    if let convertedUrl {
                                        // Here Upload video on server
                                        // For sample example here I'm playing video with AVPlayer
                                        self.playVideo(for: convertedUrl)
                                    } else {
                                        self.didTapCancelButton()
                                    }
                                }
                            }
                        }
                    }
                }
                return
            }
        }
    }
    loader.stopAnimating()
}

extension NSItemProvider {
    func load(type: UTType) async -> Any? {
        guard self.hasItemConformingToTypeIdentifier(type.identifier) else {
            return nil
        }
        do {
            let item = try await self.loadItem(forTypeIdentifier: type.identifier)
            if type == .data || type == .image {
                print("Type ", type.identifier)
                switch item {
                case let image as UIImage:
                    return image
                case let data as Data:
                    return UIImage(data: data)
                case let url as URL:
                    _ = url.startAccessingSecurityScopedResource()
                    if  let image = UIImage(contentsOfFile: url.path) {
                        return image
                    }
                    url.stopAccessingSecurityScopedResource()
                    return url
                default:
                    return nil
                }
            } else if let url = item as? URL {
                return url
            }
        } catch {
            print(error.localizedDescription)
        }
        return nil
    }
}

问题原因

  1. 资源加载顺序不合理:当前代码优先加载.data类型,系统通过NSItemProvider返回视频数据时,会自动对视频进行压缩,导致画质下降;而直接从应用选择视频时,获取的是原始文件URL,无压缩操作。
  2. UTType匹配泛化:未优先请求视频专属的UTType,而是用通用的.data/.url类型,无法确保获取到原始视频资源。

修复方案

1. 调整资源加载顺序与UTType优先级

优先请求视频专属的UTType(如.movie、.mpeg4Movie),再回退到.url和.data,确保优先获取原始视频文件URL。

2. 优化代码实现

修改后的核心代码如下:

func handleSharedFile() async {
    loader.startAnimating()
    let attachments = (self.extensionContext?.inputItems.first as? NSExtensionItem)?.attachments ?? []
    
    // 优先请求视频专属UTType
    let videoUTTypes: [UTType] = [.movie, .mpeg4Movie, .quickTimeMovie]
    for provider in attachments {
        // 尝试加载视频类型资源
        for videoUTType in videoUTTypes {
            guard let loadedItem = await provider.load(type: videoUTType) else { continue }
            if let url = loadedItem as? URL {
                await MainActor.run {
                    self.processVideoURL(url)
                }
                return
            }
        }
        
        // 视频类型加载失败,回退到URL和Data类型
        let fallbackTypes = [UTType.url, .data]
        for contentType in fallbackTypes {
            guard let loadedItem = await provider.load(type: contentType) else { continue }
            if let url = loadedItem as? URL {
                await MainActor.run {
                    self.processVideoURL(url)
                }
                return
            }
        }
    }
    loader.stopAnimating()
}

// 提取视频处理逻辑,简化代码结构
private func processVideoURL(_ url: URL) {
    if UIVideoEditorController.canEditVideo(atPath: url.path) {
        let editorController = UIVideoEditorController()
        editorController.videoMaximumDuration = 90
        editorController.videoPath = url.path
        editorController.delegate = self
        present(editorController, animated: true)
    } else {
        Task {
            guard let convertedUrl = await VideoConverterHelper().convertVideo(at: url, isShare: true) else {
                self.didTapCancelButton()
                return
            }
            self.playVideo(for: convertedUrl)
        }
    }
}

同时优化NSItemProvider扩展,针对视频类型做专门处理:

extension NSItemProvider {
    func load(type: UTType) async -> Any? {
        guard self.hasItemConformingToTypeIdentifier(type.identifier) else {
            return nil
        }
        do {
            let item = try await self.loadItem(forTypeIdentifier: type.identifier)
            
            // 视频类型资源直接返回URL
            if type.conforms(to: .video) {
                if let url = item as? URL {
                    _ = url.startAccessingSecurityScopedResource()
                    defer { url.stopAccessingSecurityScopedResource() }
                    return url
                }
            }
            // 图片和数据类型处理逻辑保持不变
            else if type == .data || type == .image {
                switch item {
                case let image as UIImage:
                    return image
                case let data as Data:
                    return UIImage(data: data)
                case let url as URL:
                    _ = url.startAccessingSecurityScopedResource()
                    defer { url.stopAccessingSecurityScopedResource() }
                    return UIImage(contentsOfFile: url.path) ?? url
                default:
                    return nil
                }
            } else if let url = item as? URL {
                return url
            }
        } catch {
            print(error.localizedDescription)
        }
        return nil
    }
}

说明

  • 优先请求视频专属UTType,能让系统直接返回未压缩的原始视频文件URL,避免自动压缩导致的画质损失。
  • 调整加载顺序后,分享扩展将优先获取原始视频资源,与主应用直接选择视频的逻辑保持一致,解决画质差异问题。
  • 使用安全作用域URL时,需确保正确调用startAccessingSecurityScopedResource()和stopAccessingSecurityScopedResource(),保证资源可正常访问。

内容的提问来源于stack exchange,提问作者Nirav D

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 18:00:05