AVFoundation 框架初探究(三)

 

 

這篇總結什麼?git


 

      在該系列的上一篇的文章中,咱們總結的大體內容以下:github

      一、視頻錄製  AVCaptureSession + AVCaptureMovieFileOutput數組

      二、視頻錄製 AVCaptureSession + AVAssetWriter數據結構

      三、AVCaptureSession + AVCaptureMovieFileOutput 與 AVCaptureSession + AVAssetWriter 的區別架構

      這是這個系列總結文章的第三篇,前面咱們提了音頻以及視頻的基本的播放,錄製等等的知識,這篇文章咱們總結開發祕籍中的第三章的內容 -- 資源和元數據。 異步

      說白了就是總結 AVAsset 這個類!ide

      

AVAsset性能


 

      AVAsset是一個抽象類(抽象類中不必定包含抽象方法,可是包含抽象方法的類必定要被聲明爲抽象類。抽象類自己不具有實際的功能,只能用於派生其子類。抽象類中能夠包含構造方法,可是構造方法不能被聲明爲抽象,簡單點的說你不能實例化一個抽象類。然而,咱們能夠嘗試複製該方案在Objective-C中採用一些技巧,要確保不能實例化你的父類),咱們前面簡單的說明了一下什麼是抽象類,咱們的AVAsset就是一個抽象類,你經過  assetWithURL 實際建立的就是他的子類,名爲 AVURLAsset ,這一段話你們仔細理解一下。優化

      一:AVAsset的異步載入  AVAsynchronousKeyValueLoading 協議ui

      這個AVAsynchronousKeyValueLoading咱們的AVAsset類是遵照了的,這個協議裏面就兩個必須實現的方法,咱們解釋一下這兩個方法:

/*
         typedef NS_ENUM(NSInteger, AVKeyValueStatus) {
         
                 AVKeyValueStatusUnknown,
                 AVKeyValueStatusLoading,
                 AVKeyValueStatusLoaded,
                 AVKeyValueStatusFailed,
                 AVKeyValueStatusCancelled
         };
         
         // 這個方法能夠用來查詢給定屬性的狀態,若是返回的這個狀態不是AVKeyValueStatusLoaded,那咱們在此刻去請求這個狀態的時候可能會出現卡頓
         - (AVKeyValueStatus)statusOfValueForKey:(NSString *)key error:(NSError * _Nullable * _Nullable)outError;
         
         // keys參數就是咱們要請求的屬性數組,當完成請求以後就會在handler這個block回調給咱們
         - (void)loadValuesAsynchronouslyForKeys:(NSArray<NSString *> *)keys completionHandler:(nullable void (^)(void))handler;
*/  

 

      咱們簡單的應用一下上面的知識,寫個很簡單的Demo,這個Demo仍是會在咱們這一系列文章的git上,咱們請求一些咱們本地數據的一些基本的屬性,代碼以下:

-(void)getAssetMessage{
        
        NSString * path = [[NSBundle mainBundle]pathForResource:@"薛之謙-像風同樣.mp3" ofType:nil];
        NSURL * url = [NSURL fileURLWithPath:path];
        
        AVAsset * asset = [AVAsset assetWithURL:url];
        NSArray * keys = @[@"duration"];
        [asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
        
                NSError * error;
                AVKeyValueStatus  status = [asset statusOfValueForKey:@"duration" error:&error];
                switch (status) {
                        case AVKeyValueStatusLoaded:
                                
                                // 要更新UI的操做須要回到主線程
                                NSLog(@"屬性載入成功,你能夠訪問了");
                                NSLog(@"duration = %.2f",CMTimeGetSeconds(asset.duration));
                                break;
                                
                        case AVKeyValueStatusLoading:
                                NSLog(@"AVKeyValueStatusLoading");
                                break;
                                
                        case AVKeyValueStatusFailed:
                                NSLog(@"AVKeyValueStatusFailed");
                                break;
                                
                        case AVKeyValueStatusUnknown:
                                NSLog(@"AVKeyValueStatusUnknown");
                                break;
                        default:
                                break;
                }
        }];
}

 

       上面的輸出的日誌以下:

 

 

      須要注意的地方在代碼註釋中有些,通過上面的代碼咱們就異步的訪問了它的duration屬性,爲何咱們訪問一個屬性都須要寫這些個代碼呢?咱們說一下緣由爲這個AVAsynchronousKeyValueLoading協議的總結畫一個句號。

      說明: 咱們之因此須要異步的訪問一些屬性,是由於屬性的訪問總結同步的發生的,若是正在請求的屬性沒有預先載入,程序就會阻塞,一直到它能夠作出適當的響應,顯然這樣必定會帶來問題,好比咱們上面說的duration屬性可能就是一個潛在的昂貴操做,若是開發者在使用MP3文件時候沒有在頭文件中設置TLEN標籤,這個標籤用於定義duration值,則整個音頻曲目都須要進行解析來準確肯定它的duration值,假設這個請求發生在主線程,那麼等待響應就會阻塞主線程,直到相關的操做完成爲止,在最好的狀況下可能會感受應用變得遲鈍,用戶界面沒有響應。

 

媒體元數據


 

      元數據的格式:

      雖然存在不少種格式的媒體資源,可是咱們在iOS的環境下遇到的媒體的類型主要就是下面的四類,咱們簡單的總結一下下面的四類,就再也不作具體的說明,有興趣的研究這些類型的能夠本身上網查查:

 

      一:QuickTime

      QuickTime 是由蘋果開發的一種功能強大、跨平臺的媒體架構。該架構的一部分是 QuickTime File Formant 規範, 定義了 .mov文件的內部結構。 QuickTime 文件由一種稱爲 atoms 的數據結構組成。

      二:MPEG-4 音頻和視頻

      MPEG-4 Part 14 是定義MP4文件格式的規範,MP4直接派生於 QuickTime 文件格式,這就意味着它與 QuickTime 文件的結構是相似的,就像QuickTime文件同樣,MP4文件也由稱爲 atom 的數據結構組成。 關於文件名再說一點, .mp4 是對MPEG-4媒體的標準擴展。但存在一些變化,如 .m4v、.m4a、.m4p 、 .m4b 等,這些變體都是使用的 MPEG-4 容器格式,但包含了附加的擴展功能。

      三:MP3 

      MP3文件與上面介紹的兩種格式有顯著的區別,MP3文件使用容器格式,而使用編碼音頻數據,包含的可選元數據的結構塊一般位於文件開頭。MP3文件使用一種稱爲ID3v2的格式來保存關於音頻內容的描述信息,包含的數據有歌曲演唱者、所屬唱片和音樂風格等等。       

      AV Foundation 支持讀取ID3v2標籤的全部版本,但不支持寫入。MP3格式收到專利限制,因此 AVFoundation 沒法支持對MP3後者ID3數據進行編碼。

 

使用元數據


 

      在大部分狀況下咱們會使用 AVAsset 提供的元數據,不過設計獲取曲目以及原數據等狀況時候也會使用 AVAssetTrack , 讀取具體的資源元數據的接口由 AVMetadataItem 這個類提供,這個類提供了一個面向對象的接口,讓開發這能夠對存儲在 QuickTime、MPeg-4 atom、ID3 幀中的元數據進行訪問。

      說一下 AVAsset 的三個屬性/方法:

      一、commonMetadata 這個屬性從Common鍵空間獲取元數據,這個屬性會返回以一個包含全部可用元數據的數組

      二、availableMetadataFormats 這個屬性會返回一個字符串數組,其中定義了資源中包含的全部的原數據格式

      三、metadataForFormat: 這個方法的參數是一個用於定義元數據格式的NSString 對象, 它的返回值是一個包含全部相關元數據信息的NSArray

      根據上面這三個方法,咱們看下面的Demo中的一個方法:

-(void)getAVMetadataItemMessage{
        
        NSString * path = [[NSBundle mainBundle]pathForResource:@"薛之謙-像風同樣.mp3" ofType:nil];
        NSURL * url = [NSURL fileURLWithPath:path];
        
        AVAsset * asset = [AVAsset assetWithURL:url];
        NSArray * keys  = @[@"availableMetadataFormats"];
        NSMutableArray * metaArray =[NSMutableArray array];
        
        // commonMetadata 從Common鍵空間獲取元數據、這個屬性會返回一個包括全部可用元數據的數組
        NSArray * commonMetaArray = [asset commonMetadata];
        NSLog(@"commonMetaArray = %@",commonMetaArray);
        
        //
        [asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
                
                // availableMetadataFormats 這個屬性會返回一個字符串
                // 其中定義了資源中包含的全部元數據格式
                for (NSString * format in asset.availableMetadataFormats) {
                    
                    // metadataForFormat 方法 這個方法包含一個用於定義元數據格式的NSString對象並返回一個包含全部先關元數據信息的NSArray
                    [metaArray addObjectsFromArray:[asset metadataForFormat:format]];
                }
                
                //
                NSLog(@"metaArray = %@",metaArray);
                // 使用 AVMetadataItem
                for (AVMetadataItem * item in metaArray) {
                        
                    NSLog(@"%@ : %@",item.key,item.value);
                }
        }];
}

 

      上面的這段代碼咱們須要注意的點在代碼的註釋中都已經提到了,下面咱們須要關心的是它的日誌。

  

 

      分析一下上面代碼的日誌:

      commonMetadata 獲取到的全部的可用的元數據的描述信息數組和經過availableMetadataFormats和metadataForFormat這兩個組合方法獲取到的元數據的描述信息是同樣的。

      還有一點和我在書中看的描述不一致的地方是 Key  和 Value 這兩個屬性的打印。按照書中的描述這樣的寫法獲取到的 Key 是整型數據,而咱們獲取到的是上面的輸出,其實在最上面的描述信息中能夠看到上面是有Key 這個屬性的,這點暫時我也沒明白,但事實是按照咱們上面的輸出日誌咱們的確是不能理解 TIT2 或者 TALA 甚至是 TPE1 這些Key表明的含義!其實他們都是MP3文件的標籤,我上往搜了一下這些標籤的含義,大體的說一下這些標籤,方便之後使用時候查閱:

 /*
                     * TEXT: 歌詞做者
                       TENC: 編碼
                       WXXX: URL連接(URL)
                       TCOP: 版權(Copyright)
                       TOPE: 原藝術家
                       TCOM: 做曲家
                       TDAT: 日期
                       TPE3: 指揮者
                       TPE2: 樂隊
                       TPE1: 藝術家至關於ID3v1的Artist
                       TPE4: 翻譯(記錄員、修改員)
                       TYER: 即ID3v1的Year
                       USLT: 歌詞
                       TSIZ: 大小
                       TALB: 專輯至關於ID3v1的Album
                       TIT1: 內容組描述
                       TIT2: 標題至關於ID3v1的Title
                       TIT3: 副標題
                       TCON: 流派(風格)至關於ID3v1的Genre
                       AENC: 音頻加密技術
                       TSSE: 編碼使用的軟件(硬件設置)
                       TBPM: 每分鐘節拍數 COMM: 註釋至關於ID3v1的Comment
                       TDLY: 播放列表返錄
                       TRCK: 音軌(曲號)至關於ID3v1的Track
                       TFLT: 文件類型
                       TIME: 時間
                       TKEY: 最初關鍵字
                       TLAN: 語言
                       TLEN: 長度
                       TMED: 媒體類型
                       TOAL: 原唱片集
                       TOFN: 原文件名
                       TOLY: 原歌詞做者
                       TORY: 最初發行年份
                       TOWM: 文件全部者(許可證者)
                       TPOS: 做品集部分
                       TPUB: 發行人
                       TRDA: 錄製日期
                       TRSN: Intenet電臺名稱
                       TRSO: Intenet電臺全部者
                       UFID: 惟一的文件標識符
                       TSRC: ISRC(國際的標準記錄代碼)
                     
                     */

 

      上面的標籤應該差很少包括了基本的標籤,要是在之後的使用中有其餘遇到的本身沒有見過的再添加進來。

 

這一章最後說的竟然是 AVAssetExportSession


      

      AVAssetExportSession 這個咱們再前面說過,在前面拍攝完視頻以後咱們就利用這個 AVAssetExportSession 壓縮視頻。AVAssetExportSession 用於將AVAsset 內容根據導出預設條件進行轉碼,並將導出資源寫到磁盤中,AVAssetExportSession 提供了多個功能來實現將一種格式轉換爲另外一個格式、修訂資源的內容、修改資源的音頻和視頻行爲,固然還有咱們最幹星期的功能,即寫入新的元數據。

      使用AVAssetExportSession實例大體須要作下面這些:

      一、須要一個AVAsset會話

      二、根據前面的AVAsset會話實例以及設置的壓縮質量初始化獲得AVAssetExportSession對象

      三、其實前面的裏能夠理解成導入設置,接下來就是導出設置,調出的地址outputURL以及outputFileType導出的格式

      四、接下來就是利用exportAsynchronouslyWithCompletionHandler方法導出了,導出的數據會在改方法的Block中回調

      五、最後就是在回調的block中根據AVAssetExportSession對象的status屬性去判斷壓縮是否成功,進而進行本身想要的操做

      上面的步驟大體上就說清楚了AVAssetExportSession,其餘的API有興趣能夠進AVAssetExportSession的.h文件去看看,下面就是咱們前面有用到的一段源碼:

#pragma mark --
#pragma mark -- 視頻壓縮方法
-(void)compressVideoWithFileUrl:(NSURL *)fileUrl{
    
    /* 
     
      這裏須要注意的一點就是在重複的路徑上保存文件是不行的,能夠選擇在點擊開始的時候刪除以前的
      也能夠這樣按照時間命名不一樣的文件保存
      在後面的 AVAssetWriter 也要注意這一點
     */
    // 壓縮後的視頻的方法命名
    NSDateFormatter * formatter = [[NSDateFormatter alloc]init];
    [formatter setDateFormat:@"yyyy-MM-dd-HH:mm:ss"];
        
    // 壓縮後的文件路徑
    self.videoPath = [NSString stringWithFormat:@"%@/%@.mov",NSTemporaryDirectory(),[formatter stringFromDate:[NSDate date]]];
    
    // 先根據你傳入的文件的路徑穿件一個AVAsset
    AVAsset * asset = [AVAsset  assetWithURL:fileUrl];
    /*
     
     根據urlAsset建立AVAssetExportSession壓縮類
     第二個參數的意義:經常使用 壓縮中等質量  AVAssetExportPresetMediumQuality
     AVF_EXPORT NSString *const AVAssetExportPresetLowQuality        NS_AVAILABLE_IOS(4_0);
     AVF_EXPORT NSString *const AVAssetExportPresetMediumQuality     NS_AVAILABLE_IOS(4_0);
     AVF_EXPORT NSString *const AVAssetExportPresetHighestQuality    NS_AVAILABLE_IOS(4_0);
    
    */
    AVAssetExportSession * exportSession = [[AVAssetExportSession alloc]initWithAsset:asset presetName:AVAssetExportPresetMediumQuality];
    
    // 優化壓縮,這個屬性能使壓縮的質量更好
    exportSession.shouldOptimizeForNetworkUse = YES;
    // 處處的文件的路徑
    exportSession.outputURL =  [NSURL fileURLWithPath:self.videoPath];
    // 導出的文件格式
    /*!
      @constant  AVFileTypeMPEG4  mp4格式的   AVFileTypeQuickTimeMovie mov格式的
      @abstract A UTI for the MPEG-4 file format.
      @discussion
      The value of this UTI is @"public.mpeg-4".
      Files are identified with the .mp4 extension.
      能夠看看這個outputFileType格式,好比AVFileTypeMPEG4也能夠寫成public.mpeg-4,其餘相似
    */
    exportSession.outputFileType = AVFileTypeQuickTimeMovie;
    
    NSLog(@"視頻壓縮後的presetName: %@",exportSession.presetName);
    // 壓縮的方法
    [exportSession exportAsynchronouslyWithCompletionHandler:^{
        
        /*
         exportSession.status 枚舉屬性
         typedef NS_ENUM(NSInteger, AVAssetExportSessionStatus) {
         AVAssetExportSessionStatusUnknown,
         AVAssetExportSessionStatusWaiting,
         AVAssetExportSessionStatusExporting,
         AVAssetExportSessionStatusCompleted,
         AVAssetExportSessionStatusFailed,
         AVAssetExportSessionStatusCancelled
         };
         */
        int exportStatus = exportSession.status;
        switch (exportStatus) {
            case AVAssetExportSessionStatusFailed:
                
                NSLog(@"壓縮失敗");
                break;
            case AVAssetExportSessionStatusCompleted:
            {
                /*
                 壓縮後的大小
                 也能夠利用exportSession的progress屬性,隨時監測壓縮的進度
                 */
                NSData * data = [NSData dataWithContentsOfFile:self.videoPath];
                float dataSize = (float)data.length/1024/1024;
                NSLog(@"視頻壓縮後大小 %f M", dataSize);
               
            }
                break;
            default:
                break;
        }
    }];
}

 

      上面的內容大體就是書中第三章的內容了,具體的Demo能夠在下面連接中下載!

      Demo下載地址

      個人博客即將同步至騰訊雲+社區,邀請你們一同入駐。

相關文章
相關標籤/搜索