AVURLAsset能否在没有文件扩展名的情况下工作?

21

我正在尝试为视频文件创建缩略图:

- (UIImage*) thumbnailForVideoAtURL: (NSURL*) videoURL
{
    AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:videoURL options:nil];
    AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
    CGImageRef imageHandle = [generator copyCGImageAtTime:kCMTimeZero actualTime:NULL error:NULL];
    if (imageHandle) {
        UIImage *frameImage = [UIImage imageWithCGImage:imageHandle];
        CFRelease(imageHandle);
        return frameImage;
    } else {
        return nil;
    }
}

问题在于视频文件存储在一个内容可寻址存储中,并且没有扩展名。这似乎使AVURLAsset无法正常工作,因为该资源被创建后,在读取帧图像时会出现以下错误:

Error Domain=AVFoundationErrorDomain Code=-11828 "Cannot Open" UserInfo=0x167170 {NSLocalizedFailureReason=This media format is not supported., NSUnderlyingError=0x163a00 "The operation couldn’t be completed. (OSStatus error -12847.)", NSLocalizedDescription=Cannot Open}

这个有没有记录或者提到过呢?我不敢相信我真的被迫通过文件名传递文件格式信息。在AVURLAsset初始化器中,options参数看起来是提供文件类型的好地方,但是根据文档,似乎没有支持该功能。

PS. 我已经测试了代码,带有正确扩展名的同一文件可以正常生成缩略图。

4个回答

16

最后,我临时创建一个硬链接到文件并给硬链接正确的扩展名。这是一个小技巧,我希望能看到更好的解决方案。我已经在 AVURLAsset 上提交了错误报告,希望苹果公司可以添加从 options 字典中读取文件格式信息的功能。


1
仍然没有在iOS7中修复,[[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];对于没有扩展名的文件没有问题。 - William Entriken
嘿..同样的方法也适用于PHAsset..我无法通过从PHAsset获取的路径读取本地文件。因此,链接起来是可行的。 - ProblemSlover
1
iOS 10.3,这个问题仍未解决。也许需要等到iOS 11呢? :D - shelll
2
iOS 11,这个问题仍未得到解决 :( - limon
3
iOS 13有同样的问题 :P - J. Doe
显示剩余4条评论

8

快速回答:

let filePath = ###YOU SET THIS###
let fileManager = NSFileManager.defaultManager()
let documentsURL = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .AllDomainsMask).last!
let tmpURL = documentsURL.URLByAppendingPathComponent("tmp.caf")
_ = try? fileManager.removeItemAtURL(tmpURL)
_ = try? fileManager.linkItemAtURL(fileURL, toURL: tmpURL)

完整的Obj-C答案:

NSString *filePath = ###YOU SET THIS###
NSFileManager *dfm = [NSFileManager defaultManager];
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *tmpPath = [[documentPaths lastObject] stringByAppendingPathComponent:@"tmp.caf"];
[dfm removeItemAtPath:tmpPath error:nil];
[dfm linkItemAtPath:filePath toPath:tmpPath error:nil];

现在AVURLAsset将与tmpPath一起使用。


嘿..相同的方法也适用于PHAsset..我无法通过从PHAsset获取的路径读取本地文件。因此,链接起来可以解决问题。 - ProblemSlover

6
let mimeType = "video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\""
let asset = AVURLAsset(url: videoURL!, options:["AVURLAssetOutOfBandMIMETypeKey": mimeType])
let playerItem = AVPlayerItem(asset: asset)
avPlayer = AVPlayer(playerItem: playerItem)
self.avPlayerViewConroller?.player = avPlayer
playerItem.addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions(), context: nil)
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
{
        if(avPlayer?.currentItem?.status == AVPlayerItemStatus.readyToPlay)
        {
            avPlayer?.play()
        }
}

这实际上是有效的,至少对于AVAssetImageGenerator。 - Ben G
1
谢谢。在声明mimeType ="audio/mp4"之前,我无法播放音频。幸运的是,它并不关心编解码器,因为即使我使用ffprobe,也无法获取文件的编解码器。 - Tristian

4
我相信我已经在iOS7中找到了一个解决方法。诀窍是使用AVAssetResourceLoader。将一个由您创建的方案包含的虚拟URL传递给AVURLAsset,这将导致调用AVAssetResourceLoaderDelegate,例如“myscheme://random.com/file.mp4”。正如您可以看到我创建的URL具有.mp4扩展名,只要您正确地按照下一步操作,这将允许AVPlayer正确打开文件。现在实现AVAssetResourceLoaderDelegate以使用实际的URL即“http://actual.com/file”,数据将正确传递到AVPlayer,而不会抱怨无法打开该文件格式。

1
我正在努力实现您推荐的解决方法,并且正在实现AVAssetResourceLoaderDelegate,但我仍然无法让AVPlayer播放音频源。您能否详细说明一下“现在实现AVAssetResourceLoaderDelegate以使用实际URL即'http://actual.com/file'”是什么意思?您是否在某种程度上交换了URL? - Shackleford
1
我之前做过这个,但基本上我使用了一个资源方案,带有 .mp4 扩展名,以便调用委托而不被跳过。一旦进入该委托,我使用实际的资源地址来加载它,我使用了一个静态方法来获取真正的资源地址,而不是使用虚拟地址,这是为了正确调用委托所必需的。 - Pellet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接