最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

在iOS中給視頻添加濾鏡的方法示例

 更新時間:2020年03月16日 08:34:25   作者:Lyman''s Blog  
這篇文章主要介紹了在iOS中給視頻添加濾鏡的方法示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

「眾所周知,視頻可以 P」,今天我們來學(xué)習(xí)怎么給視頻添加濾鏡。

在 iOS 中,對視頻進(jìn)行圖像處理一般有兩種方式: GPUImage AVFoundation

一、GPUImage

在之前的文章中,我們對 GPUImage 已經(jīng)有了一定的了解。之前一般使用它對攝像頭采集的圖像數(shù)據(jù)進(jìn)行處理,然而,它對本地視頻的處理也一樣方便。

直接看代碼:

// movie
NSString *path = [[NSBundle mainBundle] pathForResource:@"sample" ofType:@"mp4"];
NSURL *url = [NSURL fileURLWithPath:path];
GPUImageMovie *movie = [[GPUImageMovie alloc] initWithURL:url];

// filter
GPUImageSmoothToonFilter *filter = [[GPUImageSmoothToonFilter alloc] init];

// view
GPUImageView *imageView = [[GPUImageView alloc] initWithFrame:CGRectMake(0, 80, self.view.frame.size.width, self.view.frame.size.width)];
[self.view addSubview:imageView];

// chain
[movie addTarget:filter];
[filter addTarget:imageView];

// processing
[movie startProcessing];

核心代碼一共就幾行。 GPUImageMovie 負(fù)責(zé)視頻文件的讀取, GPUImageSmoothToonFilter 負(fù)責(zé)濾鏡效果處理, GPUImageView 負(fù)責(zé)最終圖像的展示。

通過濾鏡鏈將三者串起來,然后調(diào)用 GPUImageMovie 的 startProcessing 方法開始處理。

雖然 GPUImage 在使用上簡單,但是存在著 沒有聲音 、 在非主線程調(diào)用 UI 、 導(dǎo)出文件麻煩 、 無法進(jìn)行播放控制 等諸多缺點。

小結(jié):GPUImage 雖然使用很方便,但是存在諸多缺點,不滿足生產(chǎn)環(huán)境需要。

二、AVFoundation

1、 AVPlayer 的使用

首先來復(fù)習(xí)一下 AVPlayer 最簡單的使用方式:

NSURL *url = [[NSBundle mainBundle] URLForResource:@"sample" withExtension:@"mp4"];
AVURLAsset *asset = [AVURLAsset assetWithURL:url];
AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithAsset:asset];
  
AVPlayer *player = [[AVPlayer alloc] initWithPlayerItem:playerItem];
AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];

第一步先構(gòu)建 AVPlayerItem ,然后通過 AVPlayerItem 創(chuàng)建 AVPlayer ,最后通過 AVPlayer 創(chuàng)建 AVPlayerLayer 。

AVPlayerLayer 是 CALayer 的子類,可以把它添加到任意的 Layer 上。當(dāng) AVPlayer 調(diào)用 play 方法時, AVPlayerLayer 上就能將圖像渲染出來。

AVPlayer 的使用方式十分簡單。但是,按照上面的方式,最終只能在 AVPlayerLayer 上渲染出最原始的圖像。如果我們希望在播放的同時,對原始圖像進(jìn)行處理,則需要修改 AVPlayer 的渲染過程。

2、修改 AVPlayer 的渲染過程

修改 AVPlayer 的渲染過程,要從 AVPlayerItem 下手,主要分為 四步 :

第一步:自定義 AVVideoCompositing 類

AVVideoCompositing 是一個協(xié)議,我們的自定義類要實現(xiàn)這個協(xié)議。在這個自定義類中,可以獲取到每一幀的原始圖像,進(jìn)行處理并輸出。

在這個協(xié)議中,最關(guān)鍵是 startVideoCompositionRequest 方法的實現(xiàn):

// CustomVideoCompositing.m
- (void)startVideoCompositionRequest:(AVAsynchronousVideoCompositionRequest *)asyncVideoCompositionRequest {
  dispatch_async(self.renderingQueue, ^{
    @autoreleasepool {
      if (self.shouldCancelAllRequests) {
        [asyncVideoCompositionRequest finishCancelledRequest];
      } else {
        CVPixelBufferRef resultPixels = [self newRenderdPixelBufferForRequest:asyncVideoCompositionRequest];
        if (resultPixels) {
          [asyncVideoCompositionRequest finishWithComposedVideoFrame:resultPixels];
          CVPixelBufferRelease(resultPixels);
        } else {
          // print error
        }
      }
    }
  });
}

通過 newRenderdPixelBufferForRequest 方法從 AVAsynchronousVideoCompositionRequest 中獲取到處理后的 CVPixelBufferRef 后輸出,看下這個方法的實現(xiàn):

// CustomVideoCompositing.m
- (CVPixelBufferRef)newRenderdPixelBufferForRequest:(AVAsynchronousVideoCompositionRequest *)request {
  CustomVideoCompositionInstruction *videoCompositionInstruction = (CustomVideoCompositionInstruction *)request.videoCompositionInstruction;
  NSArray<AVVideoCompositionLayerInstruction *> *layerInstructions = videoCompositionInstruction.layerInstructions;
  CMPersistentTrackID trackID = layerInstructions.firstObject.trackID;
  
  CVPixelBufferRef sourcePixelBuffer = [request sourceFrameByTrackID:trackID];
  CVPixelBufferRef resultPixelBuffer = [videoCompositionInstruction applyPixelBuffer:sourcePixelBuffer];
    
  if (!resultPixelBuffer) {
    CVPixelBufferRef emptyPixelBuffer = [self createEmptyPixelBuffer];
    return emptyPixelBuffer;
  } else {
    return resultPixelBuffer;
  }
}

在這個方法中,我們通過 trackID 從 AVAsynchronousVideoCompositionRequest 中獲取到 sourcePixelBuffer ,也就是當(dāng)前幀的原始圖像。

然后調(diào)用 videoCompositionInstruction 的 applyPixelBuffer 方法,將 sourcePixelBuffer 作為輸入,得到處理后的結(jié)果 resultPixelBuffer 。也就是說,我們對圖像的處理操作,都發(fā)生在 applyPixelBuffer 方法中。

在 newRenderdPixelBufferForRequest 這個方法中,我們已經(jīng)拿到了當(dāng)前幀的原始圖像 sourcePixelBuffer ,其實也可以直接在這個方法中對圖像進(jìn)行處理。

那為什么還需要把處理操作放在 CustomVideoCompositionInstruction 中呢?

因為在實際渲染的時候,自定義 AVVideoCompositing 類的實例創(chuàng)建是系統(tǒng)內(nèi)部完成的。也就是說,我們訪問不到最終的 AVVideoCompositing 對象。所以無法進(jìn)行一些渲染參數(shù)的動態(tài)修改。而從 AVAsynchronousVideoCompositionRequest 中,可以獲取到 AVVideoCompositionInstruction 對象,所以我們需要自定義 AVVideoCompositionInstruction ,這樣就可以間接地通過修改 AVVideoCompositionInstruction 的屬性,來動態(tài)修改渲染參數(shù)。

第二步:自定義 AVVideoCompositionInstruction

這個類的關(guān)鍵點是 applyPixelBuffer 方法的實現(xiàn):

// CustomVideoCompositionInstruction.m
- (CVPixelBufferRef)applyPixelBuffer:(CVPixelBufferRef)pixelBuffer {
  self.filter.pixelBuffer = pixelBuffer;
  CVPixelBufferRef outputPixelBuffer = self.filter.outputPixelBuffer;
  CVPixelBufferRetain(outputPixelBuffer);
  return outputPixelBuffer;
}

這里把 OpenGL ES 的處理細(xì)節(jié)都封裝到了 filter 中。這個類的實現(xiàn)細(xì)節(jié)可以先忽略,只需要知道它接受 原始的 CVPixelBufferRef ,返回 處理后的 CVPixelBufferRef 。

第三步:構(gòu)建 AVMutableVideoComposition

構(gòu)建的代碼如下:

self.videoComposition = [self createVideoCompositionWithAsset:self.asset];
self.videoComposition.customVideoCompositorClass = [CustomVideoCompositing class];
- (AVMutableVideoComposition *)createVideoCompositionWithAsset:(AVAsset *)asset {
  AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoCompositionWithPropertiesOfAsset:asset];
  NSArray *instructions = videoComposition.instructions;
  NSMutableArray *newInstructions = [NSMutableArray array];
  for (AVVideoCompositionInstruction *instruction in instructions) {
    NSArray *layerInstructions = instruction.layerInstructions;
    // TrackIDs
    NSMutableArray *trackIDs = [NSMutableArray array];
    for (AVVideoCompositionLayerInstruction *layerInstruction in layerInstructions) {
      [trackIDs addObject:@(layerInstruction.trackID)];
    }
    CustomVideoCompositionInstruction *newInstruction = [[CustomVideoCompositionInstruction alloc] initWithSourceTrackIDs:trackIDs timeRange:instruction.timeRange];
    newInstruction.layerInstructions = instruction.layerInstructions;
    [newInstructions addObject:newInstruction];
  }
  videoComposition.instructions = newInstructions;
  return videoComposition;
}

構(gòu)建 AVMutableVideoComposition 的過程 主要做兩件事情 。

第一件事情,把 videoComposition 的 customVideoCompositorClass 屬性,設(shè)置為我們自定義的 CustomVideoCompositing 。

第二件事情,首先通過系統(tǒng)提供的方法 videoCompositionWithPropertiesOfAsset 構(gòu)建出 AVMutableVideoComposition 對象,然后將它的 instructions 屬性修改為自定義的 CustomVideoCompositionInstruction 類型。(就像「第一步」提到的,后續(xù)可以在 CustomVideoCompositing 中,拿到 CustomVideoCompositionInstruction 對象。)

注意:這里可以把 CustomVideoCompositionInstruction 保存下來,然后通過修改它的屬性,去修改渲染參數(shù)。

第四步:構(gòu)建 AVPlayerItem

有了 AVMutableVideoComposition 之后,后面的事情就簡單多了。

只需要在創(chuàng)建 AVPlayerItem 的時候,多賦值一個 videoComposition 屬性。

self.playerItem = [[AVPlayerItem alloc] initWithAsset:self.asset];
self.playerItem.videoComposition = self.videoComposition;

這樣,整條鏈路就串起來了, AVPlayer 在播放時,就能在 CustomVideoCompositionInstruction 的 applyPixelBuffer 方法中接收到 原始圖像的 CVPixelBufferRef 。

3、應(yīng)用濾鏡效果

這一步要做的事情是: 在 CVPixelBufferRef 上添加濾鏡效果,并輸出處理后的 CVPixelBufferRef 。

要做到這件事情,有很多種方式。包括但不限定于: OpenGL ES 、 CIImage 、 Metal 、 GPUImage 等。

為了同樣使用前面用到的 GPUImageSmoothToonFilter ,這里介紹一下 GPUImage 的方式。

關(guān)鍵代碼如下:

- (CVPixelBufferRef)renderByGPUImage:(CVPixelBufferRef)pixelBuffer {
  CVPixelBufferRetain(pixelBuffer);
  
  __block CVPixelBufferRef output = nil;
  runSynchronouslyOnVideoProcessingQueue(^{
    [GPUImageContext useImageProcessingContext];
    
    // (1)
    GLuint textureID = [self.pixelBufferHelper convertYUVPixelBufferToTexture:pixelBuffer];
    CGSize size = CGSizeMake(CVPixelBufferGetWidth(pixelBuffer),
                 CVPixelBufferGetHeight(pixelBuffer));
    
    [GPUImageContext setActiveShaderProgram:nil];
    // (2)
    GPUImageTextureInput *textureInput = [[GPUImageTextureInput alloc] initWithTexture:textureID size:size];
    GPUImageSmoothToonFilter *filter = [[GPUImageSmoothToonFilter alloc] init];
    [textureInput addTarget:filter];
    GPUImageTextureOutput *textureOutput = [[GPUImageTextureOutput alloc] init];
    [filter addTarget:textureOutput];
    [textureInput processTextureWithFrameTime:kCMTimeZero];
    
    // (3)
    output = [self.pixelBufferHelper convertTextureToPixelBuffer:textureOutput.texture
                             textureSize:size];
    
    [textureOutput doneWithTexture];
    
    glDeleteTextures(1, &textureID);
  });
  CVPixelBufferRelease(pixelBuffer);
  
  return output;
}

(1)一開始讀入的視頻幀是 YUV 格式的,首先把 YUV 格式的 CVPixelBufferRef 轉(zhuǎn)成 OpenGL 紋理。

(2)通過 GPUImageTextureInput 來構(gòu)造濾鏡鏈起點, GPUImageSmoothToonFilter 來添加濾鏡效果, GPUImageTextureOutput 來構(gòu)造濾鏡鏈終點,最終也是輸出 OpenGL 紋理。

(3)將處理后的 OpenGL 紋理轉(zhuǎn)化為 CVPixelBufferRef 。

另外,由于 CIImage 使用簡單,也順便提一下用法。

關(guān)鍵代碼如下:

- (CVPixelBufferRef)renderByCIImage:(CVPixelBufferRef)pixelBuffer {
  CVPixelBufferRetain(pixelBuffer);
  
  CGSize size = CGSizeMake(CVPixelBufferGetWidth(pixelBuffer),
               CVPixelBufferGetHeight(pixelBuffer));
  // (1)
  CIImage *image = [[CIImage alloc] initWithCVPixelBuffer:pixelBuffer]; 
  // (2)
  CIImage *filterImage = [CIImage imageWithColor:[CIColor colorWithRed:255.0 / 255 
                                  green:245.0 / 255
                                  blue:215.0 / 255
                                  alpha:0.1]];
  // (3)
  image = [filterImage imageByCompositingOverImage:image]; 
  
  // (4)
  CVPixelBufferRef output = [self.pixelBufferHelper createPixelBufferWithSize:size]; 
  [self.context render:image toCVPixelBuffer:output];
  
  CVPixelBufferRelease(pixelBuffer);
  return output;
}

(1)將 CVPixelBufferRef 轉(zhuǎn)化為 CIImage 。

(2)創(chuàng)建一個帶透明度的 CIImage 。

(3)用系統(tǒng)方法將 CIImage 進(jìn)行疊加。

(4)將疊加后的 CIImage 轉(zhuǎn)化為 CVPixelBufferRef 。

4、導(dǎo)出處理后的視頻

視頻處理完成后,最終都希望能導(dǎo)出并保存。

導(dǎo)出的代碼也很簡單:

self.exportSession = [[AVAssetExportSession alloc] initWithAsset:self.asset presetName:AVAssetExportPresetHighestQuality];
self.exportSession.videoComposition = self.videoComposition;
self.exportSession.outputFileType = AVFileTypeMPEG4;
self.exportSession.outputURL = [NSURL fileURLWithPath:self.exportPath];

[self.exportSession exportAsynchronouslyWithCompletionHandler:^{
  // 保存到相冊
  // ...
}];

這里關(guān)鍵的地方在于將 videoComposition 設(shè)置為前面構(gòu)造的 AVMutableVideoComposition 對象,然后設(shè)置好輸出路徑和文件格式后就可以開始導(dǎo)出。導(dǎo)出成功后,可以將視頻文件轉(zhuǎn)存到相冊中。

小結(jié): AVFoundation 雖然使用比較繁瑣,但是功能強(qiáng)大,可以很方便地導(dǎo)出視頻處理的結(jié)果,是用來做視頻處理的不二之選。

源碼

請到 GitHub 上查看完整代碼。

到此這篇關(guān)于在iOS中給視頻添加濾鏡的方法示例的文章就介紹到這了,更多相關(guān)iOS 視頻添加濾鏡內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 使用C語言實例描述程序中的內(nèi)聚和耦合問題

    使用C語言實例描述程序中的內(nèi)聚和耦合問題

    這篇文章主要介紹了用C語言實例描述程序中的內(nèi)聚和耦合,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-08-08
  • C語言 詳細(xì)解析時間復(fù)雜度與空間復(fù)雜度

    C語言 詳細(xì)解析時間復(fù)雜度與空間復(fù)雜度

    算法復(fù)雜度分為時間復(fù)雜度和空間復(fù)雜度。其作用: 時間復(fù)雜度是度量算法執(zhí)行的時間長短;而空間復(fù)雜度是度量算法所需存儲空間的大小
    2022-04-04
  • Clion下載安裝使用的詳細(xì)教程(Win+MinGW)

    Clion下載安裝使用的詳細(xì)教程(Win+MinGW)

    這篇文章主要介紹了Clion下載安裝使用教程(Win+MinGW),本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-08-08
  • VC小技巧匯總之對話框技巧

    VC小技巧匯總之對話框技巧

    這篇文章主要介紹了VC小技巧匯總之對話框技巧,非常實用!對于進(jìn)行VC開發(fā)有一定的參考借鑒價值,需要的朋友可以參考下
    2014-07-07
  • C++獲取特定進(jìn)程CPU使用率的實現(xiàn)代碼

    C++獲取特定進(jìn)程CPU使用率的實現(xiàn)代碼

    寫一個小程序在后臺記錄每個進(jìn)程的CPU使用情況,揪出鎖屏后占用CPU的進(jìn)程,于是自己寫了一個C++類CPUusage,方便地監(jiān)視不同進(jìn)程的CPU占用情況。本人編程還只是個新手,如有問題請多多指教
    2019-04-04
  • Qt連接數(shù)據(jù)庫并實現(xiàn)增刪改查操作

    Qt連接數(shù)據(jù)庫并實現(xiàn)增刪改查操作

    這篇文章主要為大家詳細(xì)介紹了Qt如何連接數(shù)據(jù)庫并實現(xiàn)增刪改查等基本操作,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-09-09
  • C++中const的常見用法詳解

    C++中const的常見用法詳解

    const名叫常量限定符,用來限定特定變量,以通知編譯器該變量是不可修改的,本文為大家整理了const的幾種使用,感興趣的小伙伴可以跟隨小編一起了解一下
    2023-06-06
  • c++中八大排序算法

    c++中八大排序算法

    本篇文章主要介紹了八大排序算法,詳細(xì)的介紹了八個算法思想,實現(xiàn)代碼,穩(wěn)定性,時間復(fù)雜度等,具有一定的參考價值,有需要的可以了解一下。
    2016-11-11
  • C++控制臺實現(xiàn)密碼管理系統(tǒng)

    C++控制臺實現(xiàn)密碼管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C++控制臺實現(xiàn)密碼管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-11-11
  • 詳解Matlab繪制3D玫瑰花的方法(內(nèi)附旋轉(zhuǎn)版本)

    詳解Matlab繪制3D玫瑰花的方法(內(nèi)附旋轉(zhuǎn)版本)

    這篇文章主要為大家介紹了如何利用Matlab繪制3D版的玫瑰花以及旋轉(zhuǎn)版的3D玫瑰花,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以動手試一試
    2022-03-03

最新評論

那曲县| 鄄城县| 临漳县| 天水市| 壶关县| 兴宁市| 剑阁县| 甘泉县| 镶黄旗| 宝丰县| 师宗县| 台南县| 萍乡市| 兰考县| 太和县| 南阳市| 灯塔市| 德庆县| 武汉市| 乐至县| 长宁县| 资阳市| 丰台区| 石门县| 前郭尔| 红安县| 晴隆县| 临澧县| 河曲县| 甘谷县| 和政县| 阳江市| 石屏县| 宁化县| 乌鲁木齐县| 高碑店市| 淳安县| 施甸县| 扶风县| 怀安县| 鹰潭市|