iOS 視頻壓縮(自定義壓縮參數(shù))

最近一個新需求需要將選擇的視頻進(jìn)行壓縮后上傳臼闻,且要求按照壓縮參數(shù)進(jìn)行壓縮。因此寫上這文章記錄下囤采,我總結(jié)過后寫出的關(guān)于自定義壓縮參數(shù)壓縮視頻述呐。

- (void)compressVideo:(NSURL *)videoUrl
    withVideoSettings:(NSDictionary *)videoSettings
        AudioSettings:(NSDictionary *)audioSettings
             fileType:(AVFileType)fileType
             complete:(void (^)(NSURL * _Nullable, NSError * _Nullable))complete {
  NSURL *outputUrl = [NSURL fileURLWithPath:[self buildFilePath]];
  
  AVAsset *asset = [AVAsset assetWithURL:videoUrl];
  AVAssetReader *reader = [AVAssetReader assetReaderWithAsset:asset error:nil];
  AVAssetWriter *writer = [AVAssetWriter assetWriterWithURL:outputUrl fileType:fileType error:nil];
  
  // video part
  AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
  AVAssetReaderTrackOutput *videoOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:videoTrack outputSettings:[self configVideoOutput]];
  AVAssetWriterInput *videoInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings];
  if ([reader canAddOutput:videoOutput]) {
    [reader addOutput:videoOutput];
  }
  if ([writer canAddInput:videoInput]) {
    [writer addInput:videoInput];
  }
  
  // audio part
  AVAssetTrack *audioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] firstObject];
  AVAssetReaderTrackOutput *audioOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:audioTrack outputSettings:[self configAudioOutput]];
  AVAssetWriterInput *audioInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio outputSettings:audioSettings];
  if ([reader canAddOutput:audioOutput]) {
    [reader addOutput:audioOutput];
  }
  if ([writer canAddInput:audioInput]) {
    [writer addInput:audioInput];
  }
  
  // 開始讀寫
  [reader startReading];
  [writer startWriting];
  [writer startSessionAtSourceTime:kCMTimeZero];
  
  //創(chuàng)建視頻寫入隊(duì)列
  dispatch_queue_t videoQueue = dispatch_queue_create("Video Queue", DISPATCH_QUEUE_SERIAL);
  //創(chuàng)建音頻寫入隊(duì)列
  dispatch_queue_t audioQueue = dispatch_queue_create("Audio Queue", DISPATCH_QUEUE_SERIAL);
  //創(chuàng)建一個線程組
  dispatch_group_t group = dispatch_group_create();
  //進(jìn)入線程組
  dispatch_group_enter(group);
  //隊(duì)列準(zhǔn)備好后 usingBlock
  [videoInput requestMediaDataWhenReadyOnQueue:videoQueue usingBlock:^{
    BOOL completedOrFailed = NO;
    while ([videoInput isReadyForMoreMediaData] && !completedOrFailed) {
          CMSampleBufferRef sampleBuffer = [videoOutput copyNextSampleBuffer];
      if (sampleBuffer != NULL) {
        [videoInput appendSampleBuffer:sampleBuffer];
        DLog(@"===%@===", sampleBuffer);
        CFRelease(sampleBuffer);
      }
      else {
        completedOrFailed = YES;
        [videoInput markAsFinished];
        dispatch_group_leave(group);
      }
    }
  }];
  
  dispatch_group_enter(group);
  //隊(duì)列準(zhǔn)備好后 usingBlock
  [audioInput requestMediaDataWhenReadyOnQueue:audioQueue usingBlock:^{
    BOOL completedOrFailed = NO;
    while ([audioInput isReadyForMoreMediaData] && !completedOrFailed) {
      CMSampleBufferRef sampleBuffer = [audioOutput copyNextSampleBuffer];
      if (sampleBuffer != NULL) {
        BOOL success = [audioInput appendSampleBuffer:sampleBuffer];
        DLog(@"===%@===", sampleBuffer);
        CFRelease(sampleBuffer);
        completedOrFailed = !success;
      }
      else {
        completedOrFailed = YES;
      }
    }
    
    if (completedOrFailed) {
      [audioInput markAsFinished];
      dispatch_group_leave(group);
    }
  }];
  
  //完成壓縮
  dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    if ([reader status] == AVAssetReaderStatusReading) {
      [reader cancelReading];
    }
    
    switch (writer.status) {
      case AVAssetWriterStatusWriting: {
        DLog(@"視頻壓縮完成");
        [writer finishWritingWithCompletionHandler:^{
          
          // 可以嘗試異步回至主線程回調(diào)
          if (complete) {
            complete(outputUrl,nil);
          }
          
        }];
      }
        break;
          
      case AVAssetWriterStatusCancelled:
        DLog(@"取消壓縮");
        break;
          
      case AVAssetWriterStatusFailed:
        DLog(@"===error:%@===", writer.error);
        if (complete) {
          complete(nil,writer.error);
        }
        break;
          
      case AVAssetWriterStatusCompleted: {
        DLog(@"視頻壓縮完成");
        [writer finishWritingWithCompletionHandler:^{
          
          // 可以嘗試異步回至主線程回調(diào)
          if (complete) {
            complete(outputUrl,nil);
          }
        }];
      }
        break;
          
      default:
        break;
    }
  });
}
/** 視頻解碼 */
- (NSDictionary *)configVideoOutput {

  NSDictionary *videoOutputSetting = @{
    (__bridge NSString *)kCVPixelBufferPixelFormatTypeKey:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_422YpCbCr8],
    (__bridge NSString *)kCVPixelBufferIOSurfacePropertiesKey:[NSDictionary dictionary]
  };
    
  return videoOutputSetting;
}

/** 音頻解碼 */
- (NSDictionary *)configAudioOutput {
  NSDictionary *audioOutputSetting = @{
    AVFormatIDKey: @(kAudioFormatLinearPCM)
  };
  return audioOutputSetting;
}
/// 指定音視頻的壓縮碼率,profile蕉毯,幀率等關(guān)鍵參數(shù)信息乓搬,這些參數(shù)可以根據(jù)要求自行更改
- (NSDictionary *)performanceVideoSettings {
  NSDictionary *compressionProperties = @{
    AVVideoAverageBitRateKey          : @(409600), // 碼率 400K
    AVVideoExpectedSourceFrameRateKey : @24, // 幀率
    AVVideoProfileLevelKey            : AVVideoProfileLevelH264HighAutoLevel
  };
  
  NSString *videoCodeec;
  if (@available(iOS 11.0, *)) {
      videoCodeec = AVVideoCodecTypeH264;
  } else {
      videoCodeec = AVVideoCodecH264;
  }
  NSDictionary *videoCompressSettings = @{
    AVVideoCodecKey                 : videoCodeec,
    AVVideoWidthKey                 : @640,
    AVVideoHeightKey                : @360,
    AVVideoCompressionPropertiesKey : compressionProperties,
    AVVideoScalingModeKey           : AVVideoScalingModeResizeAspectFill
  };
  
  return videoCompressSettings;
}
- (NSDictionary *)performanceAudioSettings {
  AudioChannelLayout stereoChannelLayout = {
    .mChannelLayoutTag = kAudioChannelLayoutTag_Stereo, 
    .mChannelBitmap = kAudioChannelBit_Left,
    .mNumberChannelDescriptions = 0
  };
  NSData *channelLayoutAsData = [NSData dataWithBytes:&stereoChannelLayout length:offsetof(AudioChannelLayout, mChannelDescriptions)];
  NSDictionary *audioCompressSettings = @{
    AVFormatIDKey         : @(kAudioFormatMPEG4AAC),
    AVEncoderBitRateKey   : @(49152), // 碼率 48K
    AVSampleRateKey       : @44100, // 采樣率
    AVChannelLayoutKey    : channelLayoutAsData,
    AVNumberOfChannelsKey : @(2)  // 聲道
  };
  
  return audioCompressSettings;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市代虾,隨后出現(xiàn)的幾起案子进肯,更是在濱河造成了極大的恐慌,老刑警劉巖棉磨,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件江掩,死亡現(xiàn)場離奇詭異,居然都是意外死亡乘瓤,警方通過查閱死者的電腦和手機(jī)环形,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來衙傀,“玉大人抬吟,你說我怎么就攤上這事⊥程В” “怎么了火本?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵任洞,是天一觀的道長。 經(jīng)常有香客問我发侵,道長交掏,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任刃鳄,我火速辦了婚禮盅弛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘叔锐。我一直安慰自己挪鹏,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布愉烙。 她就那樣靜靜地躺著讨盒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪步责。 梳的紋絲不亂的頭發(fā)上返顺,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天,我揣著相機(jī)與錄音蔓肯,去河邊找鬼遂鹊。 笑死,一個胖子當(dāng)著我的面吹牛蔗包,可吹牛的內(nèi)容都是我干的秉扑。 我是一名探鬼主播,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼调限,長吁一口氣:“原來是場噩夢啊……” “哼舟陆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起耻矮,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤秦躯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后淘钟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宦赠,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年米母,在試婚紗的時候發(fā)現(xiàn)自己被綠了勾扭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡铁瞒,死狀恐怖妙色,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情慧耍,我是刑警寧澤身辨,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布丐谋,位于F島的核電站,受9級特大地震影響煌珊,放射性物質(zhì)發(fā)生泄漏号俐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一定庵、第九天 我趴在偏房一處隱蔽的房頂上張望吏饿。 院中可真熱鬧,春花似錦蔬浙、人聲如沸猪落。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽笨忌。三九已至,卻和暖如春俱病,著一層夾襖步出監(jiān)牢的瞬間官疲,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工庶艾, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留袁余,地道東北人。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓咱揍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親棚饵。 傳聞我的和親對象是個殘疾皇子煤裙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容