IOS媒体资源获取与导出

2,439 阅读7分钟

往期文章

寻找IOS相册中相似图片
雷达扩散效果搜索设备
实现一个简单沙盒文件浏览器
实现一个简单的面包屑导航

什么是媒体资源

在IOS中媒体资源大致分为两类音频和视频类型(注意相册里面的视频不属于媒体),比如我们常见的iphone中的音乐与电影,放在AppleMusic和AppleTv中的媒体资源。

媒体资源的分类

首先来看一下官方给我们定义的媒体类型

typedef NS_OPTIONS(NSUInteger, MPMediaType) {
    // audio
    MPMediaTypeMusic                                                                    = 1 << 0,
    MPMediaTypePodcast                                                                  = 1 << 1,
    MPMediaTypeAudioBook                                                                = 1 << 2,
    MPMediaTypeAudioITunesU MP_API(ios(5.0), tvos(9.0), watchos(5.0), macos(10.12.2))   = 1 << 3,
    MPMediaTypeAnyAudio                                                                 = 0x00ff,
    
    // video
    MPMediaTypeMovie        MP_API(ios(5.0), tvos(9.0), watchos(5.0), macos(10.12.2))   = 1 << 8,
    MPMediaTypeTVShow       MP_API(ios(5.0), tvos(9.0), watchos(5.0), macos(10.12.2))   = 1 << 9,
    MPMediaTypeVideoPodcast MP_API(ios(5.0), tvos(9.0), watchos(5.0), macos(10.12.2))   = 1 << 10,
    MPMediaTypeMusicVideo   MP_API(ios(5.0), tvos(9.0), watchos(5.0), macos(10.12.2))   = 1 << 11,
    MPMediaTypeVideoITunesU MP_API(ios(5.0), tvos(9.0), watchos(5.0), macos(10.12.2))   = 1 << 12,
    MPMediaTypeHomeVideo    MP_API(ios(7.0), tvos(9.0), watchos(5.0), macos(10.12.2))   = 1 << 13,
    MPMediaTypeAnyVideo     MP_API(ios(5.0), tvos(9.0), watchos(5.0), macos(10.12.2))   = 0xff00,

    MPMediaTypeAny                                                                      = ~0UL
} MP_API(ios(3.0), tvos(9.0), watchos(5.0), macos(10.12.2));

通过官方给我们的定义,然后我们稍加梳理就会发现。在IOS中系统媒体资源大致分类两类,一类是音频类型的,其中包含音乐,有声读物,博客,itunsU,另一类则是视频类,其中包括电影,电视,视频博客,MV,itunesU,家庭录像带。

image.png

媒体资源获取

因为媒体资源种类过多,但是获取方式都差不多,所以这里我们通过选择音乐类型的媒体来完成媒体资源的获取。

获取所有音乐

首先构造一个查询所有音乐的Query

MPMediaQuery* query = [[MPMediaQuery alloc] init];
MPMediaPropertyPredicate *predicate = [MPMediaPropertyPredicate predicateWithValue:[NSNumber numberWithInteger:MPMediaTypeMusic] forProperty:MPMediaItemPropertyMediaType];
[query addFilterPredicate:predicate];

我们为了查询所有音乐给Query创建了一个过滤器predicate,过滤器的作用是给查询添加一个筛选条件。 这个添加的筛选条件是通过对媒体的类型进行筛选,如果其中类型是MPMediaTypeMusic的就通过筛选。

image.png

如上图所示,筛选器通过遍历表格并获取其中的MPMediaItemPropertyMediaType属性,然后用这个属性和MPMediaTypeMusic进行对比,符合要求的即筛选出来。通过上图对比,Item1,Item3,Item4符合条件要求通过筛选,

同理我们也可以通过给过滤器设置其他的过滤媒体类型来获取不同类型的媒体。比如设置过滤器value为MPMediaTypeMovie来获取电影类型的媒体。

NSArray* itemLists = [query items];

然后我们通过调用查询Query的items方法,返回筛选结果

快捷方式构造Query

当然系统也给我们提供几种特定媒体的查询query,方便我们快速构造查询器。

/// 创建匹配音乐项目的媒体查询,并按专辑名称对集合进行分组和排序
+ (MPMediaQuery *)albumsQuery;

/// 创建匹配音乐项目的媒体查询,并按艺术家姓名对集合进行分组和排序
+ (MPMediaQuery *)artistsQuery;

/// 创建匹配音乐项目的媒体查询,并按歌曲名称对集合进行分组和排序
+ (MPMediaQuery *)songsQuery;

/// 创建匹配整个库的媒体查询,并按播放列表名称对集合进行分组和排序
+ (MPMediaQuery *)playlistsQuery;

/// 创建匹配播客项目的媒体查询,并按播客名称对集合进行分组和排序
+ (MPMediaQuery *)podcastsQuery;

/// 创建匹配有声读物项目并按有声读物名称对集合进行分组和排序的媒体查询
+ (MPMediaQuery *)audiobooksQuery;

/// 创建一个媒体查询来匹配编辑项目,并按专辑名称对集合进行分组和排序
+ (MPMediaQuery *)compilationsQuery;

/// 创建匹配所有媒体项的媒体查询,并按作曲家名称对集合进行分组和排序
+ (MPMediaQuery *)composersQuery;

/// 创建匹配所有媒体项目的媒体查询,并按流派名称对集合进行分组和排序
+ (MPMediaQuery *)genresQuery;

媒体资源属性

由于媒体资源属性太多,我直接使用下表来表示

属性作用
MPMediaItemPropertyPersistentID媒体项目的持久标识符的键。
MPMediaType用于定义媒体项目类型的属性。
MPMediaItemPropertyTitle媒体项目的标题(或名称)。
MPMediaItemPropertyAlbumTitle媒体相册名称
MPMediaItemPropertyAlbumPersistentID媒体相册持久化ID
MPMediaItemPropertyArtist媒体作者信息
MPMediaItemPropertyArtistPersistentID媒体作者信息持久化ID
MPMediaItemPropertyAlbumArtist专辑作者信息
MPMediaItemPropertyAlbumArtistPersistentID专辑作者信息持久化ID
MPMediaItemPropertyGenre媒体流派
MPMediaItemPropertyGenrePersistentID流派的持久标识符
MPMediaItemPropertyComposer作曲者信息
MPMediaItemPropertyComposerPersistentID作曲者持久化ID
MPMediaItemPropertyPlaybackDurationPlaybackDuration
MPMediaItemPropertyAlbumTrackNumber专辑曲目编号
MPMediaItemPropertyAlbumTrackCount专辑曲目数
MPMediaItemPropertyDiscNumber唱片编号
MPMediaItemPropertyDiscCount唱片合集数量
MPMediaItemPropertyArtwork艺术品
MPMediaItemPropertyIsExplicit一个布尔值,指示媒体项是否包含显式内容
MPMediaItemPropertyLyrics歌词
MPMediaItemPropertyIsCompilation一个布尔值,判断媒体是否可以编辑
MPMediaItemPropertyReleaseDate发布日期
MPMediaItemPropertyBeatsPerMinute媒体项目每分钟的音乐节拍数,对应于 iTunes 中“获取信息”对话框中“信息”选项卡中的“BPM”字段。
MPMediaItemPropertyComments有关媒体项目的文本信息,对应于 iTunes 中“获取信息”对话框中“信息”选项卡中的“评论”字段。
MPMediaItemPropertyAssetURL指向媒体项目的 URL,可以从中创建 AVAsset 对象(或其他基于 URL 的 AVFoundation 对象),并根据需要使用任何选项。
MPMediaItemPropertyIsCloudItem判断是否是一个iCloud Item
MPMediaItemPropertyHasProtectedAsset一个布尔值,指示媒体项目包装在 DRM 中,不能通过标准播放 API 使用。
MPMediaItemPropertyPodcastTitle播客的标题。
MPMediaItemPropertyPodcastPersistentID播客的持久标识符。
MPMediaItemPropertyPlayCount用户播放媒体项目的次数。
MPMediaItemPropertySkipCount用户跳过播放项目的次数。
MPMediaItemPropertyRating对象在 [0...5] 范围内的用户指定评级,其中值 5 表示最有利的评级。
MPMediaItemPropertyLastPlayedDate用户播放媒体项目的最近日历日期。
MPMediaItemPropertyUserGrouping对应于 iTunes 中“获取信息”对话框中“信息”选项卡中的“分组”字段。
MPMediaItemPropertyBookmarkTime最近一次播放时用户在媒体项目中的位置。
MPMediaItemPropertyDateAdded媒体项目添加到用户媒体库的日期。
MPMediaItemPropertyPlaybackStoreID用于排队存储曲目的标识符。

获取资源的大小

我们在媒体资源的属性中找了一圈也没有发现资源的具体大小,那怎么获取媒体资源的大小?

答案是通过 AVAssetExportSession和 KVO

AVAssetExportSession获取媒体资源大小

self.assetURL = MPMediaItem.assetURL AVAsset *asset = [AVAsset assetWithURL:self.assetURL];
AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset: asset presetName:AVAssetExportPresetAppleM4A]; 
exporter.timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration);
self.fileSize = exporter.estimatedOutputFileLength;

根据MPMediaItem的assetURL属性, 构建一个AVAsset对象 根据构建的AVAsset对象,创建一个AVAssetExportSession, 设置好AVAssetExportSession的导出预设值 AVAssetExportPresetAppleM4A

注意这里不能是AVAssetExportPresetPassthrough以这个资产原有的格式导出。

然后设置导出时长为正在资源的播放时长

exporter.timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration);

如果没有设置导出时长则,预估导出大小为0 estimatedOutputFileLength 表示导出文件的估计字节大小

估算出来的文件大小和实际大小有区别,因为AVAssetExportSession进行了转码操作,所以估算的大小是按照转码之后的文件大小来计算的。

使用KVO获取MPMediaitem的文件大小属性

self.fileSize = [[self.item valueForProperty:@"fileSize"] integerValue];

使用KVO获取的资源大小等于资源以原有格式存储在手机中的大小。

媒体资源导出

同样是以music为例子,我们来完成资源的导出

AssetUrl

根据资源媒体的属性MPMediaItemPropertyAssetURL,我们可以获取在媒体在存放的资源地址。

版权问题

如果从PC端使用A账号导入歌曲或者电影到B账号的iphone中,如果A账号中的歌曲或电影部分是有版权的,那么这些有版本的歌曲或者电影就不能到播放。

在MPMediaItem中的体现是,有版本的歌曲或者电影,有作曲家,名称,封面等媒体元数据,但是没有asstURL资源路径。

视频类型有版权的文件为 .m4v 音频类型有版权的文件为 .m4p

使用AVAssetExportSession导出资源

注意因为版权问题的存在我们不能导出有版本保护的媒体资源到我们的沙盒目录中。

在我们拿到了媒体资源的AssetUrl之后,初始化一个AVAsset用来加载资源,然后设置好需要导出到的沙盒路径。

查看可以导出的文件格式

查看AVAssetExportSession的supportedFileTypes来获取支持导出的文件格式,这里我们设置导出文件的格式为AVFileTypeCoreAudioFormat也是就对应UTI类型是com.apple.coreaudio-format


(
"com.apple.quicktime-movie",
"com.apple.m4a-audio",
"public.mpeg-4",
"com.apple.m4v-video",
"public.3gpp",
"org.3gpp.adaptive-multi-rate-audio",
"com.microsoft.waveform-audio",
"public.aiff-audio",
"public.aifc-audio",
"com.apple.coreaudio-format"
)

设置预设值

presetName用于设置导出模板的预设值。 这里我们设置模板值为AVAssetExportPresetPassthrough 这个预设值可以让AssetExportSession不对资源进行重新编码的情况下进行写入到沙盒中。

完整的导出资源代码

    NSURL* outputUrl = [NSURL fileURLWithPath:path];
    AVAsset* asset = [AVAsset assetWithURL:item.assetURL];
    AVAssetExportSession *session =[[AVAssetExportSession alloc] initWithAsset:asset presetName:AVAssetExportPresetPassthrough];
    session.outputURL = [NSURL fileURLWithPath:path];
    session.outputFileType = AVFileTypeCoreAudioFormat;
    session.metadata = asset.metadata;

    [session exportAsynchronouslyWithCompletionHandler:^{
        //判断导出状态
        switch (session.status) {
            case AVAssetExportSessionStatusCompleted:{
                //导出完成
                NSLog(@"导出成功 %@",outputUrl.absoluteString);
                complete(outputUrl,YES);
            }
                break;

            case AVAssetExportSessionStatusFailed:{
                //导出失败
                NSLog(@"导出错误 %@",session.error);
                complete(nil,NO);
            }
                break;

            default:
                complete(nil,NO);
                break;
        }
    }];