let url = Bundle.main.url(forResource: "video", withExtension: "mp4")! let playerItem = AVPlayerItem(url: url) let player = AVPlayer(playerItem: playerItem) let playerLayer = AVPlayerLayer(player: player) playerLayer.frame = view.bounds view.layer.addSublayer(playerLayer) player.play()
let asset = AVAsset(url: videoURL) let reader = try! AVAssetReader(asset: asset) let videoTrack = asset.tracks(withMediaType: .video).first! //输出样本的buffer设置 let outputSettings: [String: Any] = [ kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_32BGRA) ] //从视频轨道中读取buffer样本,并输出出来 let readerOutput = AVAssetReaderTrackOutput(track: videoTrack, outputSettings: outputSettings) reader.add(readerOutput) reader.startReading() while reader.status == .reading { if let sampleBuffer = readerOutput.copyNextSampleBuffer() { // 处理样本数据 } }
处理视频帧数据,将视频数据写入文件
let writer = try! AVAssetWriter(outputURL: outputURL, fileType: .mp4) let videoTrack = asset.tracks(withMediaType: .video).first! //设置写入文件的视频编码 let outputSettings: [String: Any] = [ AVVideoCodecKey: AVVideoCodecType.h264, AVVideoWidthKey: 640, AVVideoHeightKey: 480, ] let writerInput = AVAssetWriterInput(mediaType: .video, outputSettings: outputSettings) writer.add(writerInput) let adapter = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: writerInput, sourcePixelBufferAttributes: outputSettings) writer.startWriting() writer.startSession(atSourceTime: CMTime.zero) while // 读取视频数据 { if adapter.assetWriterInput.isReadyForMoreMediaData { adapter.append(pixelBuffer, withPresentationTime: // 时间戳) } } writerInput.markAsFinished() writer.finishWriting { // 导出完成 }
/ 本地播放 // let url = Bundle.main.url(forResource: "video", withExtension: "mp4")! // let playerItem = AVPlayerItem(url: url) // 网络播放 let url = URL(string: "http://example.com/video.mp4")! let asset = AVAsset(url: url) let playerItem = AVPlayerItem(asset: asset) let player = AVPlayer(playerItem: playerItem) let playerLayer = AVPlayerLayer(player: player) playerLayer.frame = view.bounds view.layer.addSublayer(playerLayer) player.play() 另外对AVPlayer的其他常见操作还有AVPlayer的pause()、seek(to:)方法,表示暂停、快进;还可以通过KVO监听AVPlayer的状态和播放进度等信息。 AVPlayerViewController提供了常用的播放器控制器界面,包括播放/暂停按钮、播放进度条、播放时间等。可以开箱即用,方便开发。 let playerViewController = AVPlayerViewController() let url = URL(string: "http://example.com/video.mp4")! let asset = AVAsset(url: url) let playerItem = AVPlayerItem(asset: asset) let player = AVPlayer(playerItem: playerItem) playerViewController.player = player present(playerViewController, animated: true, completion: nil)
使用NSURLSession进行缓存 // 创建NSURLSessionConfiguration对象 NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; // 设置缓存策略为NSURLRequestReturnCacheDataElseLoad configuration.requestCachePolicy = NSURLRequestReturnCacheDataElseLoad; // 创建NSURLSession对象 NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration]; // 创建NSURLRequest对象 NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://example.com/video.mp4"]]; // 发起网络请求 NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { if (error) { NSLog(@"Error: %@", error); } else { // 将视频数据保存到本地缓存 NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0]; NSString *filePath = [cachePath stringByAppendingPathComponent:@"video.mp4"]; [data writeToFile:filePath atomically:YES]; // 播放视频 AVPlayerViewController *playerViewController = [[AVPlayerViewController alloc] init]; playerViewController.player = [AVPlayer playerWithURL:[NSURL fileURLWithPath:filePath]]; [self presentViewController:playerViewController animated:YES completion:nil]; } }]; [task resume];
// 创建AVAsset对象 AVAsset *asset = [AVAsset assetWithURL:[NSURL URLWithString:@"http://example.com/video.mp4"]]; // 创建AVAssetDownloadURLSessionConfiguration对象 AVAssetDownloadURLSessionConfiguration *configuration = [AVAssetDownloadURLSessionConfiguration new]; configuration.maximumActiveDownloads = 1; configuration.allowsCellularAccess = NO; // 设置缓存路径为Caches目录下的VideoCache文件夹 NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0]; NSString *cacheFolder = [cachePath stringByAppendingPathComponent:@"VideoCache"]; NSURL *cacheURL = [NSURL fileURLWithPath:cacheFolder isDirectory:YES]; configuration.destinationURL = cacheURL; // 创建AVAssetDownloadURLSession对象 AVAssetDownloadURLSession *session = [AVAssetDownloadURLSession sessionWithConfiguration:configuration assetDownloadDelegate:self delegateQueue:nil]; // 创建AVAssetDownloadTask对象 AVAssetDownloadTask *task = [session assetDownloadTaskWithURLAsset:asset assetTitle:@"video" assetArtworkData:nil options:nil]; // 启动下载任务 [task resume]; 在AVAssetDownloadDelegate协议方法中,做视频播放 当下载完成时,保存视频文件的本地路径,并使用AVPlayerViewController进行播放: - (void)URLSession:(NSURLSession *)session assetDownloadTask:(AVAssetDownloadTask *)assetDownloadTask didFinishDownloadingToURL:(NSURL *)location { // 将视频数据保存到本地缓存 NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0
import UIKit import AVFoundation import AVKit class ViewController: UIViewController { var player: AVPlayer! var playerLayer: AVPlayerLayer! override func viewDidLoad() { super.viewDidLoad() // 创建AVPlayer // 这个M3U8文件包含了所有视频文件的URL地址,AVPlayer会根据这些URL逐一请求视频文件并进行播放。 let url = URL(string: "http://example.com/video.m3u8")! player = AVPlayer(url: url) // 创建AVPlayerLayer playerLayer = AVPlayerLayer(player: player) playerLayer.frame = view.bounds view.layer.addSublayer(playerLayer) // 播放视频 player.play() } override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() // 调整AVPlayerLayer的大小 playerLayer.frame = view.bounds } }
// 播放/暂停视频 if player?.rate == 0 { player?.play() } else { player?.pause() } //快进/快退视频: let seekTime = CMTimeMakeWithSeconds(10.0, preferredTimescale: CMTimeScale(NSEC_PER_SEC)) let currentTime = player?.currentTime() let targetTime = CMTimeAdd(currentTime!, seekTime) player?.seek(to: targetTime) //播放状态发生变化时的回调函数 player?.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(1, preferredTimescale: CMTimeScale(NSEC_PER_SEC)), queue: DispatchQueue.main, using: { [weak self] (time) in // 更新播放进度 }) //播放器状态发生变化时的回调函数 player?.addObserver(self, forKeyPath: "status", options: [.old, .new], context: nil) override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "status" { if player?.status == .failed { // 播放失败 } else if player?.status == .readyToPlay { // 准备播放 } } }