多媒体开发指南:AV Foundation与音乐库操作
1. 图层与动画添加
在架构中引入更多图层是可行的。以下是添加文本图层的代码:
let lay = CATextLayer()
lay.string = "This is cool!"
lay.alignmentMode = .center
lay.foregroundColor = UIColor.black.cgColor
lay.frame = child.bounds
child.addSublayer(lay)
现在,导出的版本会在前面叠加显示 “This is cool!” 字样。
我们还可以进一步为图层添加动画。例如,让文本在视频开始一秒后慢慢淡入,代码如下:
let ba = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
ba.duration = 1
ba.fromValue = 0
ba.toValue = 1
ba.beginTime = AVCoreAnimationBeginTimeAtZero + 1 // 关键
ba.fillMode = .backwards
lay.add(ba, forKey: nil)
2. AVPlayerLayer 的使用
2.1 基本介绍
AVPlayer 并非界面对象,对应的界面对象是 AVPlayerLayer(CALayer 的子类)。它没有让用户播放、暂停电影以及查看进度的控件,仅用于显示电影,是 AV Foundation 媒体世界和用户可见的 CALayer 世界之间的桥梁。
2.2 不使用 AVPlayerViewController 显示视频
以下是不使用 AVPlayerViewController 显示视频的代码:
let m = Bundle.main.url(forResource:"ElMirage", withExtension:"mp4")!
let asset = AVURLAsset(url:m)
let item = AVPlayerItem(asset:asset)
let p = AVPlayer(playerItem:item)
self.player = p // 可能之后需要引用
let lay = AVPlayerLayer(player:p)
lay.frame = CGRect(10,10,300,200)
self.playerLayer = lay // 可能之后需要引用
self.view.layer.addSublayer(lay)
若要防止视频准备好显示时出现闪屏,可以通过 KVO 监听 isReadyForDisplay 属性,在其变为 true 后再将 AVPlayerLayer 添加到界面。
2.3 优化代码
在 WWDC 2016 视频中,Apple 建议先创建没有 AVPlayerItem 的 AVPlayer,再创建 AVPlayerLayer,最后将 AVPlayerItem 分配给 AVPlayer,代码如下:
let m = Bundle.main.url(forResource:"ElMirage", withExtension:"mp4")!
let asset = AVURLAsset(url:m)
let item = AVPlayerItem(asset:asset)
let p = AVPlayer() // *
self.player = p
let lay = AVPlayerLayer(player:p)
lay.frame = CGRect(10,10,300,200)
self.playerLayer = lay
p.replaceCurrentItem(with: item) // *
self.view.layer.addSublayer(lay)
这样做可以提高效率,因为当 AVPlayer 没有关联的 AVPlayerLayer 时,它会认为 AVAsset 只有音频轨道重要,之后再关联 AVPlayerLayer 时,就需要匆忙获取视频轨道。
2.4 控制视频播放
视频显示后,需要控制其播放。可以通过调用 play 或设置 AVPlayer 的 rate 来开始播放,以下是一个简单的播放/暂停按钮的代码:
@IBAction func doButton (_ sender: Any) {
let rate = self.player.rate
self.player.rate = rate < 0.01 ? 1 : 0
}
还可以让用户将播放头跳回电影开头,代码如下:
@IBAction func restart (_ sender: Any) {
let item = self.player.currentItem!
item.seek(to:CMTime(seconds:0, preferredTimescale:600))
}
2.5 支持画中画功能
若要让 AVPlayerLayer 支持画中画功能,需要使用 AVKit 提供的 AVPictureInPictureController,代码如下:
if AVPictureInPictureController.isPictureInPictureSupported() {
let pic = AVPictureInPictureController(playerLayer: self.playerLayer)
self.pic = pic
}
需要自己提供画中画按钮,并在点击时调用 startPictureInPicture 方法:
@IBAction func doPicInPic(_ sender: Any) {
if self.pic.isPictureInPicturePossible {
self.pic.startPictureInPicture()
}
}
还可以设置自己为 AVPictureInPictureController 的代理,以便在画中画窗口的生命周期阶段进行界面调整。
3. AV Foundation 的其他功能
3.1 主要功能列表
- 从电影中提取单张图像(缩略图),使用 AVAssetImageGenerator。
- 以不同格式导出电影,或通过缓冲区读写原始未压缩数据,使用 AVAssetExportSession、AVAssetReader、AVAssetReaderOutput、AVAssetWriter、AVAssetWriterInput 等。
- 通过设备硬件捕获音频、视频和静态图像,使用 AVCaptureSession 等。
- 接入正在捕获或播放的视频和音频,包括将视频帧捕获为静态图像,使用 AVPlayerItemVideoOutput、AVCaptureVideoDataOutput 等。
3.2 相关视频
AV Foundation 的媒体功能在经典的 WWDC 视频中得到了很好的总结: https://developer.apple.com/videos/play/wwdc2011/415/
4. UIVideoEditorController 的使用
4.1 基本介绍
UIVideoEditorController 是一个视图控制器,用于让用户修剪视频。它的视图和内部行为不受开发者控制,也不建议子类化。需要通过其代理进行响应。
4.2 使用步骤
- 调用 canEditVideo(atPath:) 方法检查视频是否可编辑:
let path = Bundle.main.path(forResource:"ElMirage", ofType: "mp4")!
let can = UIVideoEditorController.canEditVideo(atPath:path)
if !can {
print("can't edit this video")
return
}
- 创建 UIVideoEditorController 实例,设置代理和视频路径,并根据设备类型设置模态展示样式:
let vc = UIVideoEditorController()
vc.delegate = self
vc.videoPath = path
if UIDevice.current.userInterfaceIdiom == .pad {
vc.modalPresentationStyle = .popover
}
self.present(vc, animated: true)
if let pop = vc.popoverPresentationController {
let v = sender as! UIView
pop.sourceView = v
pop.sourceRect = v.bounds
pop.delegate = self
}
4.3 代理方法
需要实现三个代理方法,并在其中关闭展示的视图:
func videoEditorControllerDidCancel(_ editor: UIVideoEditorController) {
self.dismiss(animated:true)
}
func videoEditorController(_ editor: UIVideoEditorController, didFailWithError error: Error) {
self.dismiss(animated:true)
}
func videoEditorController(_ editor: UIVideoEditorController, didSaveEditedVideoToPath path: String) {
self.dismiss(animated:true)
if UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(path) {
UISaveVideoAtPathToSavedPhotosAlbum(path, self, #selector(savedVideo), nil)
} else {
// 无法保存到相册,尝试其他操作
}
}
@objc func savedVideo(at path:String, withError error:Error?, ci:UnsafeMutableRawPointer) {
if let error = error {
print("error: \(error)")
}
}
5. 音乐库操作
5.1 功能概述
iOS 设备可用于存储和播放音乐、播客和有声读物,这些构成了设备的音乐库。开发者可以使用 Media Player 框架实现以下功能:
- 探索音乐库。
- 播放音乐库中的项目。
- 控制音乐应用的音乐播放器。
- 提供标准界面让用户选择音乐库项目。
5.2 音乐库授权
5.2.1 授权步骤
- 在 Info.plist 中添加 “Privacy — Media Library Usage Description” 键(NSAppleMusicUsageDescription),向用户说明访问音乐库的原因。
- 调用 MPMediaLibrary 的 authorizationStatus 类方法检查授权状态,结果可能为:
- .notDetermined:从未请求过授权,应请求授权,系统会弹出授权提示框。
- .authorized:已获得授权,可以访问音乐库。
- .denied:被拒绝授权,若应用依赖音乐库访问,可以弹出提示框请求授权,甚至可以引导用户到设置应用中进行授权。
- .restricted:被拒绝授权,且用户可能无权授权,最好不做处理。
- 若授权状态为 .notDetermined,调用 MPMediaLibrary 的 requestAuthorization 方法请求授权,并传入完成函数:
func checkForMusicLibraryAccess(andThen f:(()->())? = nil) {
let status = MPMediaLibrary.authorizationStatus()
switch status {
case .authorized: f?()
case .notDetermined:
MPMediaLibrary.requestAuthorization() { status in
if status == .authorized {
DispatchQueue.main.async {
f?()
}
}
}
case .restricted: break // 不做处理
case .denied: break // 不做处理,或请求授权
}
}
5.3 探索音乐库
5.3.1 主要类介绍
- MPMediaEntity:抽象类,是音乐库中所有项目的基类,有两个具体子类。
- MPMediaItem:单个项目(“歌曲”)。
- MPMediaCollection:MPMediaItems 的有序列表,类似于数组。
5.3.2 属性获取
MPMediaEntity 及其子类可以通过键值对(属性)描述自身。可以使用 value(forProperty:) 方法获取单个属性的值,使用 enumerateValues(forProperties:using:) 方法获取多个属性的值。同时,它们也有与属性键名对应的实例属性,方便使用。
5.3.3 项目类型
MPMediaItem 有类型(mediaType 或 MPMediaItemPropertyMediaType),如音乐、播客或有声读物。不同类型的项目有略微不同的属性。
5.3.4 播放列表
播放列表是 MPMediaPlaylist,是 MPMediaCollection 的子类,其属性包括名称和属性集,用于判断是否为 “智能” 播放列表。
5.3.5 艺术作品图像
项目的艺术作品图像可以通过 MPMediaItemArtwork 类的实例获取,但实际获取的图像大小可能不符合预期,可能需要进一步缩放。
5.4 查询音乐库
5.4.1 形成查询
有三种主要方式形成查询:
- 无限制查询:使用 MPMediaQuery() 创建简单查询,获取音乐库中的所有内容。
- 便捷构造函数:使用 MPMediaQuery 提供的类方法,如 songs、podcasts、audiobooks 等,获取音乐库的有限子集。
- 使用过滤谓词:通过附加一个或多个 MPMediaPropertyPredicate 实例来更精确地限制查询。谓词有三个方面:属性、值和比较类型(可选)。
查询还可以根据 groupingType 对结果进行分组,可选值包括 .title、.album、.artist 等。
5.4.2 执行查询
执行查询只需获取查询的属性:
- 若不关心查询返回的组,可以获取 its items,即 MPMediaItems 数组。
- 若关心组,可以获取 its collections,即 MPMediaItemCollections 数组,每个集合代表一个组。
5.4.3 查询示例
以下是一些查询示例:
- 获取所有专辑的标题:
let query = MPMediaQuery.albums()
guard let result = query.collections else {return}
for album in result {
print(album.representativeItem!.albumTitle!)
}
- 获取名称包含 “Beethoven” 的所有专辑的标题:
let query = MPMediaQuery.albums()
let hasBeethoven = MPMediaPropertyPredicate(value:"Beethoven",
forProperty:MPMediaItemPropertyAlbumTitle,
comparisonType:.contains)
query.addFilterPredicate(hasBeethoven)
guard let result = query.collections else {return}
for album in result {
print(album.representativeItem!.albumTitle!)
}
- 获取包含名称包含 “Sonata” 的歌曲的所有专辑的标题:
let query = MPMediaQuery.albums()
let hasSonata = MPMediaPropertyPredicate(value:"Sonata",
forProperty:MPMediaItemPropertyTitle,
comparisonType:.contains)
query.addFilterPredicate(hasSonata)
guard let result = query.collections else {return}
for album in result {
print(album.representativeItem!.albumTitle!)
}
- 打印第一个匹配专辑中所有匹配歌曲的标题:
let album = result[0]
for song in album.items {
print(song.title!)
}
5.5 音乐库操作流程图
graph TD;
A[开始] --> B[检查授权状态];
B --> C{授权状态};
C -->|.notDetermined| D[请求授权];
D --> E{授权结果};
E -->|.authorized| F[访问音乐库];
C -->|.authorized| F;
C -->|.denied| G[请求授权或提示用户];
C -->|.restricted| H[不做处理];
F --> I[形成查询];
I --> J{查询方式};
J -->|无限制查询| K[获取所有内容];
J -->|便捷构造函数| L[获取有限子集];
J -->|过滤谓词| M[精确限制查询];
K --> N[执行查询];
L --> N;
M --> N;
N --> O{结果类型};
O -->|items| P[获取 MPMediaItems 数组];
O -->|collections| Q[获取 MPMediaItemCollections 数组];
P --> R[处理项目];
Q --> S[处理集合];
以上就是关于多媒体开发中 AV Foundation 和音乐库操作的详细介绍,希望能帮助开发者更好地实现相关功能。
6. 常见问题解答
6.1 AV Foundation 相关
| 问题 | 解答 |
|---|---|
| 添加 AVPlayerLayer 时出现闪屏怎么办? | 可以通过 KVO 监听其 isReadyForDisplay 属性,在该属性变为 true 后再将 AVPlayerLayer 添加到界面。 |
| 如何提高 AVPlayer 加载视频的效率? | 可以先创建没有 AVPlayerItem 的 AVPlayer,再创建 AVPlayerLayer,最后将 AVPlayerItem 分配给 AVPlayer。这样当 AVPlayer 没有关联的 AVPlayerLayer 时,它会认为 AVAsset 只有音频轨道重要,之后再关联 AVPlayerLayer 时,就不会匆忙获取视频轨道。 |
| 怎样控制视频的播放和暂停? | 可以通过调用 play 方法或设置 AVPlayer 的 rate 属性来控制视频的播放和暂停。例如: swift<br>@IBAction func doButton (_ sender: Any) {<br> let rate = self.player.rate<br> self.player.rate = rate < 0.01 ? 1 : 0<br>}<br> |
| 如何实现视频的画中画功能? | 首先要确保设备支持画中画功能,然后使用 AVKit 提供的 AVPictureInPictureController。示例代码如下: swift<br>if AVPictureInPictureController.isPictureInPictureSupported() {<br> let pic = AVPictureInPictureController(playerLayer: self.playerLayer)<br> self.pic = pic<br>}<br> 同时,需要自己提供画中画按钮,并在点击时调用 startPictureInPicture 方法。 |
6.2 UIVideoEditorController 相关
|问题|解答|
|如何判断视频是否可编辑?|调用 UIVideoEditorController 的 canEditVideo(atPath:) 方法进行检查。示例:
swift<br>let path = Bundle.main.path(forResource:"ElMirage", ofType: "mp4")!<br>let can = UIVideoEditorController.canEditVideo(atPath:path)<br>if !can {<br> print("can't edit this video")<br> return<br>}<br> |
|UIVideoEditorController 的代理方法有哪些需要实现?|需要实现三个代理方法:videoEditorControllerDidCancel( :)、videoEditorController( :didFailWithError:) 和 videoEditorController(_:didSaveEditedVideoToPath:),并在这些方法中关闭展示的视图。|
|编辑后的视频保存到哪里了?|编辑后的视频会保存到应用的临时目录,你可以根据需求将其复制到其他地方,如用户的相机胶卷相册。|
6.3 音乐库操作相关
|问题|解答|
|访问音乐库需要什么授权?|需要在 Info.plist 中添加 “Privacy — Media Library Usage Description” 键(NSAppleMusicUsageDescription),并调用 MPMediaLibrary 的 authorizationStatus 类方法检查授权状态,根据不同状态进行相应处理。|
|如何查询音乐库中的特定内容?|可以通过无限制查询、便捷构造函数或使用过滤谓词来形成查询,然后执行查询获取结果。例如,获取名称包含 “Beethoven” 的所有专辑的标题:
swift<br>let query = MPMediaQuery.albums()<br>let hasBeethoven = MPMediaPropertyPredicate(value:"Beethoven",<br> forProperty:MPMediaItemPropertyAlbumTitle,<br> comparisonType:.contains)<br>query.addFilterPredicate(hasBeethoven)<br>guard let result = query.collections else {return}<br>for album in result {<br> print(album.representativeItem!.albumTitle!)<br>}<br> |
|MPMediaItem 和 MPMediaCollection 有什么区别?|MPMediaItem 代表单个项目(如歌曲),而 MPMediaCollection 是 MPMediaItems 的有序列表,类似于数组。|
7. 性能优化建议
7.1 AV Foundation 性能优化
- 延迟添加 AVPlayerLayer :如前面提到的,通过 KVO 监听 isReadyForDisplay 属性,在视频准备好显示时再添加 AVPlayerLayer,避免闪屏问题,同时也能优化用户体验。
- 合理使用 AVPlayerItem :采用先创建无 AVPlayerItem 的 AVPlayer,再创建 AVPlayerLayer,最后分配 AVPlayerItem 的方式,提高视频加载效率。
- 异步查询 :查询音乐库可能会比较耗时,建议在后台线程进行,避免阻塞主线程。例如:
DispatchQueue.global().async {
let query = MPMediaQuery.albums()
guard let result = query.collections else { return }
DispatchQueue.main.async {
// 在主线程更新 UI
for album in result {
print(album.representativeItem!.albumTitle!)
}
}
}
7.2 音乐库操作性能优化
- 缓存查询结果 :如果某些查询结果经常使用,可以将其缓存起来,避免重复查询,提高查询效率。
- 合理使用过滤谓词 :在使用过滤谓词时,尽量减少不必要的过滤条件,避免查询过于复杂,影响性能。
8. 总结
通过对 AV Foundation 和音乐库操作的学习,我们了解了如何在应用中添加图层和动画,使用 AVPlayerLayer 播放视频,实现视频编辑和画中画功能,以及对音乐库进行授权、探索和查询等操作。同时,我们还掌握了一些常见问题的解决方法和性能优化建议。
在实际开发中,开发者可以根据具体需求选择合适的功能和方法,结合性能优化策略,为用户提供更好的多媒体体验。希望以上内容能对开发者有所帮助,让大家在多媒体开发的道路上更加顺利。
9. 未来展望
随着移动设备性能的不断提升和用户对多媒体需求的日益增长,多媒体开发领域将迎来更多的机遇和挑战。未来,我们可以期待以下方面的发展:
9.1 AV Foundation 发展趋势
- 更高质量的视频处理 :支持更高分辨率、帧率和编码格式的视频处理,为用户带来更加清晰、流畅的视频体验。
- 增强现实(AR)与视频的融合 :将 AR 技术与视频播放和编辑相结合,创造出更加沉浸式的多媒体体验。
- 智能视频分析 :利用人工智能和机器学习技术,对视频内容进行智能分析,实现自动剪辑、场景识别等功能。
9.2 音乐库操作发展趋势
- 云音乐库的深度整合 :随着云存储和流媒体技术的发展,音乐库将更多地与云服务相结合,实现跨设备的音乐同步和播放。
- 个性化音乐推荐 :通过对用户音乐偏好的分析,提供更加个性化的音乐推荐服务,提高用户的音乐发现能力。
- 音乐社交功能的增强 :增加音乐社交功能,让用户可以分享自己喜欢的音乐、创建音乐社区等,增强用户之间的互动和交流。
总之,多媒体开发领域充满了无限的可能性,开发者需要不断学习和探索,跟上技术发展的步伐,为用户带来更加优质的多媒体应用。
超级会员免费看
6880

被折叠的 条评论
为什么被折叠?



