iOS 視訊外部採集
視訊外部採集
1、使用場景
當開發者業務中出現以下情況時,我們推薦使用 SDK 的外部採集功能:
-
普通攝像頭的採集無法滿足需求。例如,包含了大量的原有業務。
-
直播過程中,開發者需要使用攝像頭完成的額外功能和 SDK 的預設邏輯有衝突,導致攝像頭無法正常使用。例如,直播到一半,需要錄製短視訊。
-
直播非攝像頭資料。例如視訊播放、螢幕分享、遊戲直播等。
這是即構科技音視訊SDK(ZegoLiveRoom SDK )高階功能系列第十篇。
2、功能簡介
考慮到裝置的獨佔問題,SDK 的視訊外部採集採用的是面向物件的設計,幫助使用者把原有采集程式碼封裝成外部採集裝置。
開發者通過實現 ZegoVideoCaptureFactory 和 ZegoVideoCaptureDevice 協議,可以把採集的資料傳給 SDK 進行編碼推流:
ZegoVideoCaptureFactory 是外部採集的入口,定義了建立、銷燬 ZegoVideoCaptureDevice 的介面,向 SDK 提供管理 ZegoVideoCaptureDevice 生命週期的能力。需要呼叫 setVideoCaptureFactory 的地方必須實現該介面。
ZegoVideoCaptureDevice 定義基本的元件能力,包括 zego_allocateAndStart、zego_stopAndDeAllocate、zego_startCapture、zego_stopCapture,方便 SDK 在直播流程中進行互動。
請注意,SDK 會在適當的時機建立和銷燬 ZegoVideoCaptureDevice,開發者無需擔心生命週期不一致的問題。
3、步驟
外部採集的介面呼叫流程如下圖所示:
3.1 建立外部採集工廠
下述程式碼展示瞭如何建立外部採集工廠。工廠儲存了 ZegoVideoCaptureDevice 的例項,不會反覆建立。
@interface ZegoVideoCaptureFactory : NSObject<ZegoVideoCaptureFactory> @end @implementation ZegoVideoCaptureFactory { ZegoVideoCaptureFromImage * g_device_; } - (id<ZegoVideoCaptureDevice>)zego_create:(NSString*)deviceId { if (g_device_ == nil) { g_device_ = [[ZegoVideoCaptureFromImage alloc]init]; } return g_device_; } - (void)zego_destroy:(id<ZegoVideoCaptureDevice>)device { } @end
請注意:
大部分情況下,ZegoVideoCaptureFactory 會快取 ZegoVideoCaptureDevice例項,開發者需避免建立新的例項,造成爭搶獨佔裝置(例如攝像頭)。
開發者必須保證 ZegoVideoCaptureDevice 在 create 和 destroy 之間是可用的,請勿直接銷燬物件。
3.2 設定外部採集工廠
開發者需要使用外部採集功能時,請在使用前呼叫 setVideoCaptureFactory: 設定外部採集工廠物件(此例中的物件為步驟 1 中所建立的 ZegoVideoCaptureFactory),該工廠物件不可為空。
setVideoCaptureFactory: 必須在 InitSDK 前呼叫。
請注意
如果使用者釋放了工廠物件,不再需要它時,請呼叫本介面將其設定為空。
若客戶端使用開關控制是否使用外部採集,在開啟外部採集後,需要先釋放 SDK 物件(直接將物件置為 nil),再重新執行外部採集的設定。
if (bUse)
{
if (g_factory == nil)
g_factory = [[VideoCaptureFactoryDemo alloc] init];
[ZegoLiveRoomApi setVideoCaptureFactory:g_factory];
}
else
{
[ZegoLiveRoomApi setVideoCaptureFactory:nil];
}
3.3 建立外部採集裝置
下述程式碼,以建立在記憶體中繪製圖片的採集裝置( ZegoVideoCaptureFromImage )為例,開發者可按各自的需求,參看實現步驟。
類定義
ZegoVideoCaptureFromImage 的類定義如下:
ZegoVideoCaptureFromImage.h
// 注意採集裝置需要遵循 ZegoVideoCaptureDevice 協議
@interface ZegoVideoCaptureFromImage : NSObject<ZegoVideoCaptureDevice>
@end
ZegoVideoCaptureFromImage.m
@implementation ZegoVideoCaptureFromImage
@end
告知 SDK 當前採集資料的型別
由於採集的多樣性,SDK 支援多種外部採集資料傳遞格式,所以開發者必須顯示告知 SDK 當前採集裝置使用何種資料傳遞型別。目前 SDK 支援的型別有:
後續示例程式碼都將以官方推薦的 CVPixelBuffer 型別來傳輸採集資料,用於演示 ZegoVideoCaptureFromImage 的使用:
- (ZegoVideoCaptureDeviceOutputBufferType)zego_supportBufferType {
return ZegoVideoCaptureDeviceOutputBufferTypeCVPixelBuffer;
}
請注意 zego_supportBufferType 是optional的,但是在使用 ZegoVideoCaptureDeviceOutputBufferTypeGlTexture2D
ZegoVideoCaptureDeviceOutputBufferTypeEncodedFrame 時必須實現。
初始化資源
開發者初始化資源在 zego_allocateAndStart 中進行。
開發者在 zego_allocateAndStart 中獲取到 client (SDK 內部實現的、同樣實現 ZegoVideoCaptureClientDelegate 協議的客戶端),用於通知 SDK 採集結果。
SDK 會在 App 第一次預覽 / 推流 / 拉流時呼叫 zego_allocateAndStart。除非 App 中途呼叫過 zego_stopAndDeAllocate,否則 SDK 不會再呼叫 zego_allocateAndStart。
#pragma mark - ZegoVideoCaptureDevice
- (void)zego_allocateAndStart:(id<ZegoVideoCaptureClientDelegate>)client {
client_ = client;
is_take_photo_ = false;
}
釋放資源
開發者釋放資源在 zego_stopAndDeAllocate 中進行。
建議同步停止採集任務後清理 client 物件,保證 SDK 呼叫 zego_stopAndDeAllocate 後,沒有殘留的非同步任務導致野指標 crash。
- (void)zego_stopAndDeAllocate {
[client_ destroy]; // 必須 destroy client
client_ = nil;
}
請注意,開發者必須在 zego_stopAndDeAllocate 方法中呼叫 client 的 destroy 方法,否則會造成記憶體洩漏。
啟動採集
推流成功後,開發者需要在 zego_startCapture 中,採集資料並傳遞給 SDK 的 client 物件。
這裡演示的是啟動一個定時器,按照幀率定時觸發傳送資料。
// SDK 推流成功後,呼叫 zego_startCapture 通知外部採集裝置把採集到的影象傳給 client 物件。
- (int)zego_startCapture {
if(m_oState.capture) {
// * already started
return 0;
}
m_oState.capture = true;
dispatch_async(dispatch_get_main_queue(), ^{
[g_fps_timer invalidate];
int fps = m_oSettings.fps > 0 ?: 15;
fps = 15;
NSLog(@"%s, fps: %d", __func__, fps);
// 啟動一個定時器,按照幀率定時觸發傳送資料
g_fps_timer = [NSTimer scheduledTimerWithTimeInterval:1.0/fps target:self selector:@selector(handleTick) userInfo:nil repeats:YES];
if (pb) {
CVPixelBufferRelease(pb);
pb = NULL;
}
});
return 0;
}
定時器觸發後,外部採集裝置在記憶體中繪製圖像,並通過 client 的 onIncomingCapturedData:withPresentationTimeStamp: 方法,把資料傳給 SDK 進行編碼推流。
- (void)handleTick {
if (pb) {
CVPixelBufferRelease(pb);
pb = NULL;
}
if (!pb) {
UIImage *img = [UIImage imageNamed:@"[email protected]"];
pb = [self pixelBufferFromCGImage:img.CGImage];
}
struct timeval tv_now;
gettimeofday(&tv_now, NULL);
unsigned long long t = (unsigned long long)(tv_now.tv_sec) * 1000 + tv_now.tv_usec / 1000;
CMTime pts = CMTimeMakeWithSeconds(t, 1000);
// SDK 接受外部採集的視訊幀資料
[client_ onIncomingCapturedData:pb withPresentationTimeStamp:pts];
}
採集的影象資料存放在 CVPixelBufferRef 結構體中。
-
顏色格式推薦使用:kCVPixelFormatType_32BGRA 和
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange。 -
時間戳推薦使用:原始採集 API 返回的時間戳(如果是攝像頭,可直接透傳;如果是其他方式,可以使用系統當前時間,精度至少為毫秒)。
停止採集
開發者需要在 zego_startCapture 中停止外部採集裝置的採集工作。
這裡演示的是停止定時器。
// SDK停止推流時,呼叫 zego_stopCapture 通知外部採集裝置停止工作
- (int)zego_stopCapture {
if(!m_oState.capture) {
// * capture is not started
return 0;
}
m_oState.capture = false;
dispatch_async(dispatch_get_main_queue(), ^{
[g_fps_timer invalidate];
g_fps_timer = nil;
});
return 0;
}
上述的示例程式碼均可以在 Demo 中的 ZegoVideoCaptureFromImage.h 和 ZegoVideoCaptureFromImage.m 找到,具體細節不再贅述。