九色国产,午夜在线视频,新黄色网址,九九色综合,天天做夜夜做久久做狠狠,天天躁夜夜躁狠狠躁2021a,久久不卡一区二区三区

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
iOS 視頻外部濾鏡

1 使用場景

當(dāng) SDK 自帶的美顏無法滿足需求,例如需要做掛件、貼紙,或者美顏效果無法達到預(yù)期時,建議開發(fā)者使用外部濾鏡功能。

對于比較復(fù)雜的場景,例如想要用攝像頭畫面做圖層混合,建議開發(fā)者使用外部采集方案,這樣性能優(yōu)化的空間會更大。

2 功能簡介

考慮到濾鏡的性能問題和美顏廠商的多樣性,SDK 的視頻外部濾鏡采用面向?qū)ο笤O(shè)計,結(jié)合線程模型,幫助用戶把外部代碼封裝成可替換的濾鏡組件。

主要結(jié)構(gòu)如下:

  1. ZegoVideoFilterFactory 是外部濾鏡的入口,定義了創(chuàng)建、銷毀 ZegoVideoFilter 接口,向 SDK 提供管理 ZegoVideoFilter 生命周期的能力。需要調(diào)用 setVideoFilterFactory 的地方必須實現(xiàn)該接口。

  2. ZegoVideoFilter 定義最基本的組件功能,包括 zego_allocateAndStart、zego_stopAndDeAllocate,方便 SDK 在直播流程中進行交互。

請注意,SDK會在適當(dāng)?shù)臅r機創(chuàng)建和銷毀 ZegoVideoFilter,開發(fā)者無需擔(dān)心生命周期不一致的問題。

3 選擇合適外部濾鏡

為了實現(xiàn)傳輸不同數(shù)據(jù)模型,適配不同線程模型,同時避免實現(xiàn)多余接口,SDK 采用偽 COM 的設(shè)計方式。

開發(fā)者需要在子類中顯式指定一種數(shù)據(jù)傳遞類型,SDK 目前支持的類型有:

濾鏡類型說明
ZegoVideoBufferTypeAsyncPixelBuffer異步濾鏡(異步傳遞 BGRA32 的 CVPixelBufferRef)
ZegoVideoBufferTypeAsyncI420PixelBuffer異步 I420 濾鏡(異步傳遞 I420 的 CVPixelBufferRef)
ZegoVideoBufferTypeSyncPixelBuffer同步濾鏡(同步傳遞 BGRA32 的 CVPixelBufferRef)

SDK 會根據(jù)數(shù)據(jù)類型,實例化不同類型的 client,在調(diào)用 zego_allocateAndStart 時傳給外部濾鏡。

下面會按照不同的濾鏡類型,分別介紹用法。

請注意:

如果外部濾鏡沒有明顯的性能問題,使用同步濾鏡可以減少數(shù)據(jù)拷貝的次數(shù)。但如果比較耗時,請使用異步的方式傳遞數(shù)據(jù),以保證低端機型可以流暢運行。

請開發(fā)者結(jié)合業(yè)務(wù)特點,選擇合適的濾鏡類型。

4 異步濾鏡

4.1 創(chuàng)建外部濾鏡工廠

下述代碼演示了如何創(chuàng)建外部濾鏡工廠。工廠保存了 ZegoVideoFilter 的實例,不會反復(fù)創(chuàng)建。

@interface ZegoVideoFilterFactoryDemo : NSObject<ZegoVideoFilterFactory>@end@implementation ZegoVideoFilterFactoryDemo {    id<ZegoVideoFilter> g_filter_;}- (id<ZegoVideoFilter>)zego_create {    if (g_filter_ == nil) {         g_filter_ = [[ZegoVideoFilterDemo alloc]init]; // 此處的 ZegoVideoFilterDemo 是異步濾鏡    }    return g_filter_;}- (void)zego_destroy:(id<ZegoVideoFilter>)filter {    if (g_filter_ == filter) {        g_filter_ = nil;    }}

請注意:

  1. 大部分情況下,ZegoVideoFilterFactory 會緩存原有 ZegoVideoFilter 實例,開發(fā)者需避免創(chuàng)建新的實例。
  2. 開發(fā)者必須保證 ZegoVideoFilter 在 create 和 destroy 之間是可用的,請勿直接銷毀對象。

4.2 設(shè)置外部濾鏡工廠

開發(fā)者需要使用外部濾鏡功能時,請在使用前調(diào)用 setVideoFilterFactory: 設(shè)置外部濾鏡工廠對象(此例中的對象為步驟 1 中所創(chuàng)建的 ZegoVideoFilterFactoryDemo)。該工廠對象不可為空。

請注意,如果用戶釋放了工廠對象,不再需要它時,請調(diào)用本接口將其設(shè)置為空。

if (bUse){    {        if (g_filterFactory == nullptr)            g_filterFactory = [[ZegoVideoFilterFactoryDemo alloc] init];        [ZegoLiveRoomApi setVideoFilterFactory:g_filterFactory];    }    else    {        [ZegoLiveRoomApi setVideoFilterFactory:nil];    }}

4.3 創(chuàng)建外部濾鏡

下述代碼,以創(chuàng)建異步拷貝圖像的濾鏡為例,開發(fā)者可根據(jù)需求,參考如下實現(xiàn)步驟。

類定義

ZegoVideoFilterDemo 的類定義如下:

// 注意異步濾鏡設(shè)備需要實現(xiàn) ZegoVideoFilter、ZegoVideoBufferPool 協(xié)議ZegoVideoCaptureFromImage.h@interface ZegoVideoFilterDemo : NSObject<ZegoVideoFilter, ZegoVideoBufferPool>@endZegoVideoCaptureFromImage.m@interface ZegoVideoFilterDemo()@property (atomic) int pendingCount;    // 未處理幀數(shù)@end@implementation ZegoVideoFilterDemo {    id<ZegoVideoFilterClient> client_;     //     id<ZegoVideoBufferPool> buffer_pool_;    dispatch_queue_t queue_;    int width_;    int height_;    int stride_;    CVPixelBufferPoolRef pool_;    int buffer_count_;}

指定濾鏡類型

外部濾鏡需要根據(jù) supportBufferType 的類型,對 client 進行轉(zhuǎn)型:

  1. ZegoVideoBufferTypeAsyncPixelBuffer: SDK 按照 ZegoVideoBufferPool 調(diào)用外部濾鏡。
  2. ZegoVideoBufferTypeAsyncI420PixelBuffer:SDK 按照 ZegoVideoBufferPool 調(diào)用外部濾鏡,與 Async 型濾鏡只是圖像顏色空間有所區(qū)別。
  3. ZegoVideoBufferTypeSyncPixelBuffer: SDK 按照 ZegoVideoFilterDelegate 調(diào)用外部濾鏡。

正常來說,如果 SDK 是異步調(diào)用外部濾鏡,外部濾鏡完成前處理后,也按照同樣的步驟回調(diào) SDK。

在本例中,開發(fā)者需要顯式在 supportBufferType 中指定當(dāng)前使用的濾鏡類型為異步濾鏡:

- (ZegoVideoBufferType)supportBufferType {    // 返回濾鏡的類型,此濾鏡為:異步濾鏡    return ZegoVideoBufferTypeAsyncPixelBuffer;}

初始化資源

開發(fā)者初始化資源在 zego_allocateAndStart 中進行。

開發(fā)者在 zego_allocateAndStart 中獲取到 client(SDK 內(nèi)部實現(xiàn)的、同樣實現(xiàn) ZegoVideoFilterClient 協(xié)議的對象),用于通知 SDK 處理結(jié)果。

SDK 會在 App 第一次預(yù)覽/推流/拉流時調(diào)用 zego_allocateAndStart。除非 App 中途調(diào)用過 zego_stopAndDeAllocate,否則 SDK 不會再調(diào)用 zego_allocateAndStart。

- (void)zego_allocateAndStart:(id<ZegoVideoFilterClient>) client {    client_ = client;    if ([client_ conformsToProtocol:@protocol(ZegoVideoBufferPool)]) {        buffer_pool_ = (id<ZegoVideoBufferPool>)client;    }    width_ = 0;    height_ = 0;    stride_ = 0;    pool_ = nil;    buffer_count_ = 4;    self.pendingCount = 0;    queue_ = dispatch_queue_create("video.filter", nil);}

請注意,client 必須保存為強引用對象,在 zego_stopAndDeAllocate 被調(diào)用前必須一直被保存,SDK 不負責(zé)管理 client 的生命周期

釋放資源

開發(fā)者釋放資源在 zego_stopAndDeAllocate 中進行。

建議同步停止濾鏡任務(wù)后再清理 client 對象,保證 SDK 調(diào)用 zego_stopAndDeAllocate 后,沒有殘留的異步任務(wù)導(dǎo)致野指針 crash。

- (void)zego_stopAndDeAllocate {    dispatch_sync(queue_, ^ {    });    if (pool_) {        [ZegoLiveRoomApi destroyPixelBufferPool:&pool_];    }    queue_ = nil;    [client_ destroy];    // 必須 destroy client    client_ = nil;    buffer_pool_ = nil;}

請注意,開發(fā)者必須在 zego_stopAndDeAllocate 方法中調(diào)用 client 的 destroy 方法,否則會造成內(nèi)存泄漏。

SDK 請求外部濾鏡返回 CVPixelBufferRef,拷貝原始數(shù)據(jù)

SDK 先調(diào)用 ZegoVideoBufferPool 的 dequeueInputBuffer:height:stride: 方法,通知外部濾鏡當(dāng)前采集圖像的寬高,并請求外部濾鏡返回一個 CVPixelBufferRef 用于拷貝內(nèi)存數(shù)據(jù)。

// SDK 回調(diào)。從 App 獲取 CVPixelBufferRef 對象,用于保存原始視頻幀數(shù)據(jù)- (CVPixelBufferRef)dequeueInputBuffer:(int)width height:(int)height stride:(int)stride {    // * 按需創(chuàng)建 CVPixelBufferPool    if (width_ != width || height_ != height || stride_ != stride) {        if (pool_) {            [ZegoLiveRoomApi destroyPixelBufferPool:&pool_];        }        if ([ZegoLiveRoomApi createPixelBufferPool:&pool_ width:width height:height]) {            width_ = width;            height_ = height;            stride_ = stride;        } else {            return nil;        }    }    // * 如果處理不及時,未處理幀超過了 pool 的大小,則丟棄該幀    if (self.pendingCount >= buffer_count_) {        return nil;    }    CVPixelBufferRef pixel_buffer = nil;    CVReturn ret = CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pool_, &pixel_buffer);    if (ret != kCVReturnSuccess) {        return nil;    } else {        self.pendingCount = self.pendingCount + 1;        // * 返回一個可以用于存儲采集到的圖像的 CVPixelBuffer 實例        return pixel_buffer;    }}

外部濾鏡處理數(shù)據(jù)

當(dāng) SDK 拷貝完數(shù)據(jù)給外部濾鏡后,會調(diào)用 queueInputBuffer:timestamp:方法通知外部濾鏡。

外部濾鏡應(yīng)當(dāng)按照約定的數(shù)據(jù)傳遞類型,切換線程,異步處理。

此處的演示代碼沒有做任何操作,只是在另一個線程進行數(shù)據(jù)拷貝。開發(fā)者應(yīng)該按照各自需求,實現(xiàn)該方法。

// SDK 回調(diào)。App 在此接口中獲取 SDK 采集到的視頻幀數(shù)據(jù),并進行處理- (void)queueInputBuffer:(CVPixelBufferRef)pixel_buffer timestamp:(unsigned long long)timestamp_100n {    // * 采集到的圖像數(shù)據(jù)通過這個傳進來,這個點需要異步處理    dispatch_async(queue_, ^ {        [self copyPixelBufferToPool:pixel_buffer timestamp:timestamp_100n];        CVPixelBufferRelease(pixel_buffer);    });}

外部濾鏡處理完數(shù)據(jù),返回給 SDK

外部濾鏡處理完數(shù)據(jù)后,拷貝給 SDK 的流程和 SDK 調(diào)用外部濾鏡的步驟類似:

  1. App 調(diào)用 dequeueInputBuffer:height:stride: 向 SDK 請求 CVPixelBufferRef 作為拷貝目標(biāo)
  2. 執(zhí)行拷貝
  3. App 調(diào)用 queueInputBuffer:timestamp: 通知 SDK 拷貝完畢
// App 將處理好的數(shù)據(jù),傳遞回 SDK- (void)copyPixelBufferToPool:(CVPixelBufferRef)output timestamp:(unsigned long long)timestamp_100n{    int imageWidth = 0;    int imageHeight = 0;    int imageStride = 0;    if (output) {        imageWidth = (int)CVPixelBufferGetWidth(output);        imageHeight = (int)CVPixelBufferGetHeight(output);        imageStride = (int)CVPixelBufferGetBytesPerRowOfPlane(output, 0);    }    // * 處理完圖像,需要從 buffer pool 中取出一個 CVPixelBuffer 實例,把處理過的圖像數(shù)據(jù)拷貝到該實例中    CVPixelBufferRef dst = [buffer_pool_ dequeueInputBuffer:imageWidth height:imageHeight stride:imageStride];    if (!dst) {        return ;    }    if ([ZegoLiveRoomApi copyPixelBufferFrom:output to:dst]) {        // * 把從 buffer pool 中得到的 CVPixelBuffer 實例傳進來        [buffer_pool_ queueInputBuffer:dst timestamp:timestamp_100n];    }    self.pendingCount = self.pendingCount - 1;}

上述步驟的示例代碼可以在 ZegoVideoFilterDemo.h 和 ZegoVideoFilterDemo.m 找到,具體細節(jié)不再贅述。

5 異步 I420 濾鏡

5.1 創(chuàng)建外部濾鏡工廠

下述代碼演示了如何創(chuàng)建外部濾鏡工廠。工廠保存了 ZegoVideoFilter 的實例,不會反復(fù)創(chuàng)建。

@interface ZegoVideoFilterFactoryDemo : NSObject<ZegoVideoFilterFactory>@end@implementation ZegoVideoFilterFactoryDemo {    id<ZegoVideoFilter> g_filter_;}- (id<ZegoVideoFilter>)zego_create {    if (g_filter_ == nil) {         g_filter_ = [[ZegoVideoFilterI420Demo alloc]init]; // 此處的 ZegoVideoFilterI420Demo 是異步 I420 濾鏡    }    return g_filter_;}- (void)zego_destroy:(id<ZegoVideoFilter>)filter {    if (g_filter_ == filter) {        g_filter_ = nil;    }}

請注意:

  1. 大部分情況下,ZegoVideoFilterFactory 會緩存原有 ZegoVideoFilter 實例,開發(fā)者需避免創(chuàng)建新的實例。

  2. 開發(fā)者必須保證 ZegoVideoFilter 在 create 和 destroy 之間是可用的,請勿直接銷毀對象。

5.2 設(shè)置外部濾鏡工廠

開發(fā)者需要使用外部濾鏡功能時,請在使用前調(diào)用 setVideoFilterFactory: 設(shè)置外部濾鏡工廠對象(此例中的對象為步驟 1 中所創(chuàng)建的 ZegoVideoFilterFactoryDemo)。該工廠對象不可為空。

請注意,如果用戶釋放了工廠對象,不再需要它時,請調(diào)用本接口將其設(shè)置為空。

if (bUse){    {        if (g_filterFactory == nullptr)            g_filterFactory = [[ZegoVideoFilterFactoryDemo alloc] init];        [ZegoLiveRoomApi setVideoFilterFactory:g_filterFactory];    }    else    {        [ZegoLiveRoomApi setVideoFilterFactory:nil];    }}

5.3 創(chuàng)建外部濾鏡

下述代碼,以創(chuàng)建異步拷貝圖像的濾鏡為例,開發(fā)者可根據(jù)需求,參看實現(xiàn)步驟。

類定義

ZegoVideoFilterI420Demo 的類定義如下:

// 注意異步濾鏡設(shè)備需要實現(xiàn) ZegoVideoFilter、ZegoVideoBufferPool 協(xié)議ZegoVideoCaptureFromImage.h@interface ZegoVideoFilterI420Demo : NSObject<ZegoVideoFilter, ZegoVideoBufferPool>@endZegoVideoCaptureFromImage.m@interface ZegoVideoFilterI420Demo()@property (atomic) int pendingCount;@end@implementation ZegoVideoFilterI420Demo {    id<ZegoVideoFilterClient> client_;    id<ZegoVideoBufferPool> buffer_pool_;    dispatch_queue_t queue_;    int width_;    int height_;    int stride_;    CVPixelBufferPoolRef pool_;    int buffer_count_;}

指定濾鏡類型

外部濾鏡需要根據(jù) supportBufferType 的類型,對 client 進行轉(zhuǎn)型:

  1. ZegoVideoBufferTypeAsyncPixelBuffer: SDK 按照 ZegoVideoBufferPool 調(diào)用外部濾鏡。
  2. ZegoVideoBufferTypeAsyncI420PixelBuffer:SDK 按照 ZegoVideoBufferPool 調(diào)用外部濾鏡,與 Async 型濾鏡只是圖像顏色空間有所區(qū)別。
  3. ZegoVideoBufferTypeSyncPixelBuffer: SDK 按照 ZegoVideoFilterDelegate 調(diào)用外部濾鏡。

正常來說,如果 SDK 是異步調(diào)用外部濾鏡,外部濾鏡完成前處理后,也按照同樣的步驟回調(diào)SDK。

在本例中,開發(fā)者需要顯式在 supportBufferType 中指定當(dāng)前使用的濾鏡類型為異步濾鏡:

- (ZegoVideoBufferType)supportBufferType {    // 返回濾鏡的類型,此濾鏡為:異步 I420 濾鏡    return ZegoVideoBufferTypeAsyncI420PixelBuffer;}

初始化資源

開發(fā)者初始化資源在 zego_allocateAndStart 中進行。

開發(fā)者在 zego_allocateAndStart 中獲取到 client(SDK 內(nèi)部實現(xiàn)的、同樣實現(xiàn) ZegoVideoFilterClient 協(xié)議的對象),用于通知 SDK 處理結(jié)果。

SDK 會在 App 第一次預(yù)覽/推流/拉流時調(diào)用 zego_allocateAndStart。除非 App 中途調(diào)用過 zego_stopAndDeAllocate,否則 SDK 不會再調(diào)用 zego_allocateAndStart。

- (void)zego_allocateAndStart:(id<ZegoVideoFilterClient>) client {    client_ = client;    if ([client_ conformsToProtocol:@protocol(ZegoVideoBufferPool)]) {        buffer_pool_ = (id<ZegoVideoBufferPool>)client;    }    width_ = 0;    height_ = 0;    stride_ = 0;    pool_ = nil;    buffer_count_ = 4;    self.pendingCount = 0;    queue_ = dispatch_queue_create("video.filter", nil);}

釋放資源

開發(fā)者釋放資源在 zego_stopAndDeAllocate 中進行。

建議同步停止濾鏡任務(wù)后再清理 client 對象,保證 SDK 調(diào)用 zego_stopAndDeAllocate 后,沒有殘留的異步任務(wù)導(dǎo)致野指針 crash。

- (void)zego_stopAndDeAllocate {    if (queue_) {        dispatch_sync(queue_, ^ {        });        queue_ = nil;    }    if (pool_) {        [self destroyPool:&pool_];        pool_ = nil;    }    if (client_) {        [client_ destroy];        client_ = nil;        buffer_pool_ = nil;    }}

SDK 請求外部濾鏡返回 CVPixelBufferRef,拷貝原始數(shù)據(jù)

SDK 先調(diào)用 ZegoVideoBufferPool 的 dequeueInputBuffer:height:stride: 方法,通知外部濾鏡當(dāng)前采集圖像的寬高,并請求外部濾鏡返回一個 CVPixelBufferRef 用于拷貝內(nèi)存數(shù)據(jù)。

請注意:

ZegoVideoBufferTypeAsyncI420PixelBuffer 和 ZegoVideoBufferTypeAsyncPixelBuffer 非常相似,僅僅只是傳遞的 CVPixelBufferRef 的顏色空間為 I420,在 iOS API里面對應(yīng)的是 kCVPixelFormatType_420YpCbCr8Planar。

// SDK 回調(diào)。從 App 獲取 CVPixelBufferRef 對象,用于保存視頻幀數(shù)據(jù)- (CVPixelBufferRef)dequeueInputBuffer:(int)width height:(int)height stride:(int)stride {    if (width_ != width || height_ != height || stride_ != stride) {        if (pool_) {            [self destroyPool:&pool_];        }        // 注意此處的 createPool:height:pool 中針對 I420 顏色空間做了特殊處理,類型為 kCVPixelFormatType_420YpCbCr8Planar        if ([self createPool:width height:height pool:&pool_]) {            width_ = width;            height_ = height;            stride_ = stride;        } else {            return nil;        }    }    // * 如果處理不及時,未處理幀超過了 pool 的大小,則丟棄該幀    if (self.pendingCount >= buffer_count_) {        return nil;    }    CVPixelBufferRef pixel_buffer = nil;    CVReturn ret = CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pool_, &pixel_buffer);    if (ret != kCVReturnSuccess) {        return nil;    } else {        self.pendingCount = self.pendingCount + 1;        // * 返回一個可以用于存儲采集到的圖像的 CVPixelBuffer 實例        return pixel_buffer;    }}

外部濾鏡處理數(shù)據(jù),結(jié)束后返回給 SDK

  1. 當(dāng) SDK 拷貝完數(shù)據(jù)給外部濾鏡后,會調(diào)用 queueInputBuffer:timestamp: 方法通知外部濾鏡。
  2. 外部濾鏡處理完數(shù)據(jù)后,拷貝給 SDK 的流程和 SDK 調(diào)用外部濾鏡的步驟類似:
    • App 調(diào)用 dequeueInputBuffer:height:stride: 向 SDK 請求 CVPixelBufferRef 作為拷貝目標(biāo)
    • 執(zhí)行拷貝
    • App 調(diào)用 queueInputBuffer:timestamp: 通知 SDK 拷貝完畢

外部濾鏡應(yīng)當(dāng)按照約定的數(shù)據(jù)傳遞類型,切換線程,異步處理。

請注意,此處的演示代碼沒有做任何操作,只是在另一個線程進行數(shù)據(jù)拷貝。開發(fā)者應(yīng)該按照各自業(yè)務(wù)需求,差異化實現(xiàn)該方法。

// SDK 回調(diào)。App 在此接口中獲取 SDK 采集到的視頻幀數(shù)據(jù),并進行處理- (void)queueInputBuffer:(CVPixelBufferRef)pixel_buffer timestamp:(unsigned long long)timestamp_100n {    dispatch_async(queue_, ^ {        int imageWidth = (int)CVPixelBufferGetWidth(pixel_buffer);        int imageHeight = (int)CVPixelBufferGetHeight(pixel_buffer);        int imageStride = (int)CVPixelBufferGetBytesPerRowOfPlane(pixel_buffer, 0);        // * App 需要從 buffer pool 中取出一個 CVPixelBuffer 實例,把處理過的圖像數(shù)據(jù)拷貝到該實例中        CVPixelBufferRef dst = [buffer_pool_ dequeueInputBuffer:imageWidth height:imageHeight stride:imageStride];        if (!dst) {            return ;        }        // add your own code here        if ([self copyVideoFrame:pixel_buffer toPixelBuffer:dst]) {            // * 把從 buffer pool 中得到的 CVPixelBuffer 實例傳進來            [buffer_pool_ queueInputBuffer:dst timestamp:timestamp_100n];        }        self.pendingCount = self.pendingCount - 1;        CVPixelBufferRelease(pixel_buffer);    });}

上述步驟的示例代碼可以在 ZegoVideoFilterDemo.h 和 ZegoVideoFilterDemo.m 找到,具體細節(jié)不再贅述。

6 同步濾鏡

6.1 創(chuàng)建外部濾鏡工廠

下述代碼演示了如何創(chuàng)建外部濾鏡工廠。工廠保存了 ZegoVideoFilter 的實例,不會反復(fù)創(chuàng)建。

@interface ZegoVideoFilterFactoryDemo : NSObject<ZegoVideoFilterFactory>@end@implementation ZegoVideoFilterFactoryDemo {    id<ZegoVideoFilter> g_filter_;}- (id<ZegoVideoFilter>)zego_create {    if (g_filter_ == nil) {         g_filter_ = [[ZegoVideoFilterDemo2 alloc]init]; // 此處的 ZegoVideoFilterDemo2 是同步濾鏡    }    return g_filter_;}- (void)zego_destroy:(id<ZegoVideoFilter>)filter {    if (g_filter_ == filter) {        g_filter_ = nil;    }}

請注意:

  1. 大部分情況下,ZegoVideoFilterFactory 會緩存原有 ZegoVideoFilter 實例,開發(fā)者需避免創(chuàng)建新的實例。

  2. 開發(fā)者必須保證 ZegoVideoFilter 在 create 和 destroy 之間是可用的,請勿直接銷毀對象。

6.2 設(shè)置外部濾鏡工廠

開發(fā)者需要使用外部濾鏡功能時,請在使用前調(diào)用 setVideoFilterFactory: 設(shè)置外部濾鏡工廠對象(此例中的對象為步驟 1 中所創(chuàng)建的 ZegoVideoFilterFactoryDemo)。該工廠對象不可為空。

請注意,如果用戶釋放了工廠對象,不再需要它時,請調(diào)用本接口將其設(shè)置為空。

if (bUse){    {        if (g_filterFactory == nullptr)            g_filterFactory = [[ZegoVideoFilterFactoryDemo alloc] init];        [ZegoLiveRoomApi setVideoFilterFactory:g_filterFactory];    }    else    {        [ZegoLiveRoomApi setVideoFilterFactory:nil];    }}

6.3 創(chuàng)建外部濾鏡

下述代碼,以創(chuàng)建同步拷貝圖像的濾鏡為例,開發(fā)者可根據(jù)需求,參看實現(xiàn)步驟。

類定義

ZegoVideoFilterDemo2 的類定義如下:

// 注意同步濾鏡設(shè)備需要實現(xiàn) ZegoVideoFilter、ZegoVideoFilterDelegate 協(xié)議ZegoVideoCaptureFromImage.h@interface ZegoVideoFilterDemo2 : NSObject<ZegoVideoFilter, ZegoVideoFilterDelegate>@endZegoVideoCaptureFromImage.m@implementation ZegoVideoFilterDemo2 {    id<ZegoVideoFilterClient> client_;    id<ZegoVideoFilterDelegate> delegate_;    ZegoImageFilter* filter_;}

指定濾鏡類型

外部濾鏡需要根據(jù) supportBufferType 的類型,對 client 進行轉(zhuǎn)型:

  1. ZegoVideoBufferTypeAsyncPixelBuffer: SDK 按照 ZegoVideoBufferPool 調(diào)用外部濾鏡。
  2. ZegoVideoBufferTypeAsyncI420PixelBuffer:SDK 按照 ZegoVideoBufferPool 調(diào)用外部濾鏡,與 Async 型濾鏡只是圖像顏色空間有所區(qū)別。
  3. ZegoVideoBufferTypeSyncPixelBuffer: SDK 按照 ZegoVideoFilterDelegate 調(diào)用外部濾鏡。

正常來說,如果 SDK 是異步調(diào)用外部濾鏡,外部濾鏡完成前處理后,也按照同樣的步驟回調(diào)SDK。

在本例中,開發(fā)者需要顯式在 supportBufferType 中指定當(dāng)前使用的濾鏡類型為同步濾鏡:

- (ZegoVideoBufferType)supportBufferType {    // 返回濾鏡的類型,此濾鏡為:同步濾鏡    return ZegoVideoBufferTypeSyncPixelBuffer;}

初始化資源

開發(fā)者初始化資源在 zego_allocateAndStart中進行。

開發(fā)者在 zego_allocateAndStart 中獲取到 client(SDK 內(nèi)部實現(xiàn)的、同樣實現(xiàn) ZegoVideoFilterClient 協(xié)議的對象),用于通知 SDK 處理結(jié)果。

SDK 會在 App 第一次預(yù)覽/推流/拉流時調(diào)用 zego_allocateAndStart。除非 App 中途調(diào)用過 zego_stopAndDeAllocate,否則 SDK 不會再調(diào)用 zego_allocateAndStart。

- (void)zego_allocateAndStart:(id<ZegoVideoFilterClient>) client {    client_ = client;    if ([client_ conformsToProtocol:@protocol(ZegoVideoFilterDelegate)]) {        delegate_ = (id<ZegoVideoFilterDelegate>)client;    }    filter_ = [[ZegoImageFilter alloc] init];    [filter_ create];    [filter_ setCustomizedFilter:ZEGO_FILTER_CUSTOM_LOMO];}

釋放資源

開發(fā)者釋放資源在 zego_stopAndDeAllocate 中進行。

建議停止濾鏡任務(wù)后再清理 client 對象,保證 SDK 調(diào)用 zego_stopAndDeAllocate 后,沒有殘留的異步任務(wù)導(dǎo)致野指針 crash。

- (void)zego_stopAndDeAllocate {    [filter_ destroy];    filter_ = nil;    [client_ destroy];    // 必須 destroy client    client_ = nil;    delegate_ = nil;}

SDK 與外部濾鏡間同步傳遞數(shù)據(jù)

同步外部濾鏡數(shù)據(jù)傳遞的流程比較簡單:

  1. SDK 調(diào)用 ZegoVideoFilterDelegate的onProcess:withTimeStatmp: 方法同步傳遞圖像數(shù)據(jù)給外部濾鏡。
  2. 外部濾鏡根據(jù)約定同步完成前處理。
  3. 外部濾鏡調(diào)用 onProcess:withTimeStatmp: 方法把處理后的數(shù)據(jù)傳給 SDK。
// SDK 回調(diào)。同步傳遞圖像數(shù)據(jù)給外部濾鏡- (void)onProcess:(CVPixelBufferRef)pixel_buffer withTimeStatmp:(unsigned long long)timestamp_100 {    // * 采集到的圖像數(shù)據(jù)通過這個傳進來,同步處理完返回處理結(jié)果    CVPixelBufferRef output = [filter_ render:pixel_buffer];  // filter_ 為 Demo 實現(xiàn)的外部濾鏡示例對象    [delegate_ onProcess:output withTimeStatmp:timestamp_100]; // 外部濾鏡將處理后的數(shù)據(jù)傳遞回 SDK}

上述步驟的示例代碼可以在 ZegoVideoFilterDemo.h 和 ZegoVideoFilterDemo.m 找到,具體細節(jié)不再贅述。

7 Q&A

  1. 如何訪問 CVPixelBufferRef 持有的圖像數(shù)據(jù)?

    請參考 ZegoLiveRoomApi 的 copyPixelBufferFrom:to: 方法,然后對照蘋果官方頭文件。

  2. ZegoVideoFilterFactory 的子類什么時候釋放?

    我們推薦把工廠的實例保存為單例,僅作為 SDK 管理外部濾鏡生命周期的通道,開發(fā)者可以為工廠子類添加 setter 和 getter,一起管理濾鏡的生命周期。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
即時通訊的作用是什么?如何擁有即時通訊功能
遠程醫(yī)療解決方案,即構(gòu)遠程醫(yī)療SDK集成指引
iOS友盟第三方登錄分享
極光文檔客戶端集成插件
8 個最好的 Android 開發(fā)者工具
中國移動放出基于 Android 的 OPhone SDK
更多類似文章 >>
生活服務(wù)
熱點新聞
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服