在swift 3中播放部分下载的视频

时间:2017-11-30 07:30:59

标签: swift3 alamofire avplayer video-processing video-encoding

我需要播放视频的特定部分(例如,形式为10秒到20秒)。 所以我使用Alamofire.download(videoUrl, method: .get, headers: ["Range":String(format: "bytes=%d-%d", startByte, endByte)], to: destination).response { (response) in completionHandler(response.destinationURL) } 和自定义标题范围来下载视频的这一部分:

avplayer

到目前为止一切顺利,我可以看到下载的文件。

但是当我尝试播放时,videoAsset.loadValuesAsynchronously(forKeys: [ObserverContexts.urlAssetDurationKey, ObserverContexts.urlAssetPlayableKey])将无法执行此操作,我正在使用

let durationStatus = self.videoAsset.statusOfValue(forKey: ObserverContexts.urlAssetDurationKey, error: &durationError) guard durationStatus == .loaded else { Logger.log.error("durationStatus not loaded: \(self.videoId)") self.delegate?.onLoadError(error: . durationStatusNotLoaded) return }

加载特定键异步,但无法加载持续时间状态:

In [5]: from psycopg2.extras import NumericRange

In [6]: a = NumericRange(2, 8, '[)')

In [7]: a.lower
Out[7]: 2

In [8]: a.upper
Out[8]: 8

所以此错误会被触发,我无法播放视频。

我不是视频文件中的主人,但我认为视频文件有标题,部分下载会破坏此标题,播放器无法播放。

所以任何建议或想法(即使是最简单的)都会受到赞赏。

提前tnx。

编辑:视频标题肯定有问题,如果我从视频开始下载10秒后,视频播放10秒,但是结束视频时间或持续时间错误,我该如何解决?

2 个答案:

答案 0 :(得分:-1)

这不是原始问题的答案,但我设法通过使用AVFoundation实现我的目标,现在我可以下载我想要的确切部分,它是可玩的:

let downloadUrl = URL(string: videoUrl)!
let asset = AVURLAsset(url: downloadUrl)
let composition = AVMutableComposition()
let videoTrack = composition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID:Int32(kCMPersistentTrackID_Invalid))
let audioTrack = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: Int32(kCMPersistentTrackID_Invalid))
let tracks = asset.tracks(withMediaType: AVMediaTypeVideo)

guard let assetVideoTrack = tracks.first else {
    Logger.log.debug("video track is not available: \(videoId)")
    completionHandler(nil)
    return
}

let startCM = CMTime(seconds: startSecond, preferredTimescale: 1)
//let endCM = CMTime(seconds: startSecond  + duration, preferredTimescale: 1)
//EDITED
let endCM = CMTime(seconds: duration, preferredTimescale: 1)
let timeRange = CMTimeRangeMake(startCM, endCM)

do {
    try videoTrack.insertTimeRange(timeRange, of: assetVideoTrack, at: CMTimeMake(0, 1))
} catch let errorInsertingVideo {
    Logger.log.debug("can not get video time range: \(videoId) -- \(errorInsertingVideo.localizedDescription)")
    completionHandler(nil)
    return
}

videoTrack.preferredTransform = assetVideoTrack.preferredTransform

guard let assetAudioTrack : AVAssetTrack = asset.tracks(withMediaType: AVMediaTypeAudio).first else {
    Logger.log.debug("audio track is not available: \(videoId)")
    completionHandler(nil)
    return
}

do {
    try audioTrack.insertTimeRange(timeRange, of: assetAudioTrack, at: CMTimeMake(0, 1))
}catch let errorInsertingAudio {
    Logger.log.debug("can not get audio time range: \(videoId) -- \(errorInsertingAudio.localizedDescription)")
    completionHandler(nil)
    return
}

guard let destinationUrl = self.getDestinationUrl(videoId) else {
    // can not access video cache dir
    Logger.log.error("can not access video cache dir")
    completionHandler(nil)
    return
}

let exportSession : AVAssetExportSession = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetPassthrough)!

exportSession.outputURL = destinationUrl
exportSession.timeRange = timeRange
exportSession.outputFileType = AVFileTypeQuickTimeMovie

exportSession.exportAsynchronously(completionHandler: { 
    switch exportSession.status {
    case .completed:
        let output = exportSession.outputURL!
        completionHandler(output)
    case .failed:
        completionHandler(nil)
        break
    default :
        completionHandler(nil)
        break
    }
})

通过这个技巧,我可以从视频的中间(或开始)的任何秒开始下载恰好10秒(可选)的视频。

编辑:好的,此代码可以下载中间部分,它只适用于您从视频开头开始。

编辑2 :修复已添加。

答案 1 :(得分:-1)

我不认为IOS或Swift提供了从中间下载图像然后播放它的功能。您可以以位的形式下载视频文件内容,然后可以使用任何文件名的扩展名保存它们。 但是,除了您下载的这些字节之外,视频文件还包含大量数据,例如标题,类型,持续时间等。如果您下载的文件不包含该数据,则您的视频将无法播放。

在这种情况下,我认为(99%肯定)您将不得不使用原生代码而不仅仅是Swift将视频从特定时间解码到您想要的时间。

有很多免费的图书馆可供选择。一种这样的库是ffmpeg。也请检查this

一切顺利。