# 视频管理

# 视频数据采集管理

# 设置要开启的摄像头类型

视频采集设置前,可以通过 JCMediaDevice 类中的属性获取摄像头列表、当前摄像头以及默认摄像头,具体如下:

/// 摄像头列表
NSArray<JCMediaDeviceCamera*> *  __nonnull cameras;

/// 正在使用的摄像头
JCMediaDeviceCamera* __nullable camera;

/// 默认摄像头
JCMediaDeviceCamera* __nullable defaultCamera;

其中,摄像头对象(JCMediaDeviceCamera)有以下属性。

/// 摄像头id
NSString* __nonnull cameraId;

/// 摄像头名字
NSString* __nonnull cameraName;

/// 摄像头类型
JCMediaDeviceCameraType cameraType;

摄像头类型(JCMediaDeviceCameraType)有以下几种。

JCMediaDeviceCameraTypeNone = 0,
JCMediaDeviceCameraTypeFront = 1,
JCMediaDeviceCameraTypeBack = 2,
JCMediaDeviceCameraTypeUnknown = 3

摄像头列表获取后,可以调用下面的方法切换指定的摄像头。

/// 切换指定摄像头
/// @param camera 摄像头
-(bool)switchCamera:(JCMediaDeviceCamera* __nonnull)camera;

# 设置摄像头采集分辨率

您可以通过自定义摄像头采集参数实现不同的视频分辨率,如采集的高度、宽度和帧速率。

摄像头采集属性设置接口如下:

/**
 *  @breif 设置摄像头采集属性
 *  @param width 采集宽度,默认640
 *  @param height 采集高度,默认360
 *  @param framerate 帧速率,默认30
 */
- (void)setCameraProperty:(int)width height:(int)height framerate:(int)framerate;

示例代码

// 获取摄像头列表
NSArray<JCMediaDeviceCamera*> * cameras = mediaDevice.cameras;
// 设置要切换的摄像头
[mediaDevice switchCamera:cameras[1]];
// 设置摄像头采集属性
[mediaDevice setCameraProperty:640 height:360 framerate:30];

# 原始视频数据

在视频传输过程中,可以对每帧视频数据进行图像处理,以实现美颜等需求。有以下两种处理时机:

  1. 在视频采集后编码前处理;会影响本地预览和对端接收视频。

  2. 在解码后渲染前处理;影响本地接收视频。

具体如下:

视频采集后,编码前处理

参考如下步骤,在您的项目中实现原始视频数据功能:

  1. 发起业务前通过 Zmf_VideoCaptureAddCallback 注册视频采集回调,并在该函数中实现一个 ZmfVideoCaptureCallback 类型的回调函数

  2. 成功注册后,JC SDK 会在捕捉到每个视频帧时通过回调函数回调采集到的原始视频数据相对应参数

  3. 用户拿到视频数据后,根据场景需要自行在回调函数中进行参数处理,处理后数据通过该回调函数返回给 JC SDK。

首先注册视频采集回调,在登录成功后即可调用。

/** add capture data callback
 * @param[in] pUser     the callback user data
 * @param[in] pfnCb     the callback
 * returns 0 on succeed, otherwise failed
 */
int Zmf_VideoCaptureAddCallback (void *pUser, ZmfVideoCaptureCallback pfnCb)

回调类型说明。

/** the callback to receive captured image
 * iImgAngle - iCamOrient equal to device rotate angle.
 * if encoder is NULL, the pixel format of buf must be ZmfPixelFormatI420
 *
 * @param[in] pUser     the user data registered by Zmf_VideoCaptureAddCallback
 * @param[in] captureId the id of captured image
 * @param[in] iFace     the capture Face @see ZmfVideoFaceType
 * @param[in] iImgAngle the image rotated angle (CW)
 * @param[in] iCaptureOrient the capturer fixed orient
 * @param[in,out] iWidth  the image width
 * @param[in,out] iHeight the image height
 * @param[in,out] buf     the image data I420 buffer
 * @param[in,out] encoder capture encoder
 */
 typedef void (*ZmfVideoCaptureCallback)(void* pUser, const char* captureId, int iFace,int iImgAngle, int iCaptureOrient, int* iWidth, int* iHeight,unsigned char *buf, ZmfVideoCaptureEncoder* encoder);

示例代码

id render; //采集的视频数据对象
void* p = (__bridge void *)render;
static void zmfVideoCaptureCallback(void* pUser, const char* captureId, int iFace,int iImgAngle, int iCaptureOrient, int* iWidth, int* iHeight,unsigned char *buf, ZmfVideoCaptureEncoder* encoder) {
    NSLog(@"视频数据处理");
}
- (void)videoCall {
    //注册回调
    Zmf_VideoCaptureAddCallback(p, zmfVideoCaptureCallback);
    //发起呼叫
    [call call:@"对端号码" video:true extraParam:@"自定义透传字符串"];
}

注册后,每帧采集的视频数据通过 ZmfVideoCaptureCallback 回调,可以处理对应的视频数据。

如果想移除回调,调用下面的接口。

/** remove capture data callback
  * @param[in] pUser     the callback user data
  * returns 0 on succeed, otherwise failed
  */
int Zmf_VideoCaptureRemoveCallback (void *pUser)

示例代码

id render; //采集的视频数据对象
void* p = (__bridge void *)render;
-(void)endCall {
    //移除回调
    Zmf_VideoCaptureRemoveCallback(p);
    //挂断通话
    [call term:item reason:JCCallReasonNone description:@"自己挂断"];
}

解码后,渲染前处理

参考如下步骤,在您的项目中实现原始视频数据功能:

  1. 发起业务前通过 Zmf_VideoRenderAddCallback 注册视频输出回调,并在该函数中实现一个 ZmfVideoRenderCallback 类型的回调函数

  2. 成功注册后,JC SDK 会在捕捉到每个视频帧时通过回调函数回调输出的原始视频数据相对应参数

  3. 用户拿到视频数据后,根据场景需要自行在回调函数中进行参数处理,处理后数据通过该回调函数返回给JC SDK。

首先注册视频输出回调,在登录成功后即可调用。

/**
 * add render data callback
 *
 * @param[in] pUser      the callback user data
 * @param[in] pfnCb      the callback
 *
 * @return               0 on succeed, otherwise failed.
 */
int Zmf_VideoRenderAddCallback (void *pUser, ZmfVideoRenderCallback pfnCb);

回调类型说明。

/**
 * The callback to receive video render data
 *
 * @param[in] pUser         the user data registered by Zmf_AddVideoRenderCallback
 * @param[in] renderId      video render unique name
 * @param[in] sourceType    video render source type @see ZmfVideoSourceType
 * @param[in] iAngle the image angle
 * @param[in] iMirror the image mirror type
 * @param[in] iWidth  the image width
 * @param[in] iHeight  the image height
 * @param[in] buf           I420 render data
 *
 * @return                  if process render data should return > 0, other 0
 *
 * @remarks
 *  if buf == 0 or iWidth ==0 or iHeight == 0, means the render will close,
 *  so should call Zmf_OnVideoRenderRequestRemove.
 */
 typedef int  (*ZmfVideoRenderCallback)(void* pUser, const char* renderId, int sourceType, int iAngle,int iMirror, int* iWidth, int* iHeight, unsigned char *buf,unsigned long timeStamp);

注册后,每帧解码后的视频数据通过 ZmfVideoRenderCallback 回调,可以处理对应的视频数据。

示例代码

id render; //解码后的视频数据对象
void* p = (__bridge void *)render;
static void zmfVideoRenderCallback(void* pUser, const char* renderId, int sourceType, int iAngle,int iMirror, int* iWidth, int* iHeight, unsigned char *buf,unsigned long timeStamp) {
    NSLog(@"视频数据处理");
}
- (void)videoCall {
    //注册回调
    Zmf_VideoRenderAddCallback(p, zmfVideoRenderCallback);
    //发起呼叫
    [call call:@"对端号码" video:true extraParam:@"自定义透传字符串"];
}

如果想移除回调,调用下面的接口。

/**
 * remove render data callback
 *
 * @param[in] pUser      the callback user data
 * @return               0 on succeed, otherwise failed.
 */
int Zmf_VideoRenderRemoveCallback (void *pUser)

示例代码

id render; //解码后的视频数据对象
void* p = (__bridge void *)render;
-(void)endCall {
    //移除回调
    Zmf_VideoRenderRemoveCallback(p);
    //挂断通话
    [call term:item reason:JCCallReasonNone description:@"自己挂断"];
}

# 自定义视频输入源

在视频通话中,您可以自定义视频输入源,菊风提供将视频文件作为视频源的方案以实现该功能:

  1. 调用 startVideoFile() (opens new window) 开启该功能。(调用 stopVideoFile() (opens new window) 关闭该功能)
  2. 调用 setVideoFileFrame() (opens new window) 逐帧添加视频数据。
  3. 进行 视频画面渲染
// 开启以视频文件作为视频源的功能
[mediaDevice startVideoFile];
// 添加视频数据方法 1:
[mediaDevice setVideoFileFrame:data format:JCMediaDeviceVideoPixelFormatI420 width:640 height:360];
// 添加视频数据方法 2:
[mediaDevice setVideoFileFrame:data angle:90 mirror:1];
// 渲染画面
...

TIP

startVideoFile() 函数会自动关闭摄像头。

参数介绍

参数 描述
data 画面二进制数据
format 视频像素格式。当 format 为 H264 (opens new window) 格式并且是关键帧,则需要将 0x67 0x68 0x41 的数据作为一帧传入,并且关键帧要以固定间隔传入,例如5秒,否则一开始可能有几秒对端无法显示视频。
width
height
angle 视频旋转角度,90 的倍数
mirror 0 不镜像,1进行左右镜像
keyFrame 是否为关键帧,针对 format 为 H264 (opens new window)
- true 是关键帧
- false 不是关键帧

# 视频渲染管理

# 创建本地和远端视频画面

  • 本地视频渲染

本地视频渲染通过调用 startCameraVideo 接口获得本地视频对象用于 UI 界面显示,该接口会打开摄像头

/**
 *  @brief 获得预览视频对象,通过此对象能获得视图用于UI显示
 *  @param type 渲染模式,@ref JCMediaDeviceRender
 *  @return JCMediaDeviceVideoCanvas 对象
 */
-(JCMediaDeviceVideoCanvas* __nullable)startCameraVideo:(int)type;

其中,渲染模式(JCMediaDeviceRender)有以下三种:

名称

描述

JCMediaDeviceRenderFullScreen = 0

视频图像按比例填充整个渲染区域(裁剪掉超出渲染区域的部分区域)

JCMediaDeviceRenderFullContent

视频图像的内容完全呈现到渲染区域(可能会出现黑边,类似放电影的荧幕)

JCMediaDeviceRenderFullAuto

自动

  • 远端视频渲染

您可以调用 startVideo 方法获取对端视频对象并进行渲染。

/**
 *  @brief 获得预览视频对象,通过此对象能获得视图用于UI显示
 *  @param videoSource 渲染标识串,比如 JCMediaChannelParticipant JCCallItem 中的 renderId,当videoSource 为 videoFileId 时,内部会调用 startVideoFile
 *  @param type        渲染模式,@ref JCMediaDeviceRender
 *  @return JCMediaDeviceVideoCanvas 对象
 */
-(JCMediaDeviceVideoCanvas* __nullable)startVideo:(NSString* __nonnull)videoSource renderType:(int)type;

示例代码

// 创建本地视频画面对象
JCMediaDeviceVideoCanvas *local = [mediaDevice startCameraVideo:JCMediaDeviceRenderFullContent];
local.videoView.frame = CGRectMake(0, 0, 100, 100);
[self.view addSubview:local.videoView];

// 创建远端视频画面对象,renderId来源于通话对象,一对一为JCCallItem对象,多方为JCMediaChannelParticipant对象
JCMediaDeviceVideoCanvas *remote = [mediaDevice startVideo:renderId renderType:JCMediaDeviceRenderFullContent];
remote.videoView.frame = CGRectMake(100, 0, 100, 100);
[self.view addSubview:remote.videoView];

# 销毁本地和远端视频画面

在视频通话结束或者视频通话中,如果想销毁视频画面,可以调用下面的接口。

/**
 *  @brief 停止视频
 *  @param canvas JCMediaDeviceVideoCanvas 对象,由 startVideo 获得
 */
-(void)stopVideo:(JCMediaDeviceVideoCanvas* __nonnull)canvas;

示例代码

JCMediaDeviceVideoCanvas *localCanvas = [mediaDevice startCameraVideo:JCMediaDeviceRenderFullContent];
JCMediaDeviceVideoCanvas *remoteCanvas = [mediaDevice startVideo:renderId renderType:JCMediaDeviceRenderFullContent];
if (localCanvas) {
    // 移除本地视频
    [mediaDevice stopVideo:localCanvas];
    [localCanvas.videoView removeFromSuperview];
    localCanvas = nil;
}
if (remoteCanvas) {
    // 移除远端视频
    [mediaDevice stopVideo:remoteCanvas];
    [remoteCanvas.videoView removeFromSuperview];
    remoteCanvas = nil;
}

渲染控制主要使用到 JCMediaDeviceVideoCanvas 类中的接口。具体如下:

# 更新视频渲染标识

如果想更新视频渲染标识,可以调用下面的接口。

/**
 *  @breif 更新视频渲染标识
 *  @param videoSource 视频源
 *  @return 成功返回 true,失败返回 false
 */
-(bool)replace:(NSString*)videoSource;

# 暂停渲染

如果想暂停画面的渲染可以调用如下接口。

/**
 *  @brief 暂停渲染
 *  @return 成功返回 true,失败返回 false
 */
-(void)pause;

# 恢复渲染

如果想对已暂停的画面继续进行渲染,可以调用下面的接口。

/**
 *  @brief 恢复渲染
 *  @return 成功返回 true,失败返回 false
 */
-(void)resume;

# 视频设备管理

视频设备管理主要用到 JCMediaDevice 类中的方法,具体如下:

# 开启关闭摄像头

/**
 *  @breif 开启摄像头,一般在只需开启摄像头时调用
 *  @return 成功返回 true,失败返回 false
 */
-(bool)startCamera;

/**
 *  @breif 关闭摄像头,一般和 startCamera 配对使用
 *  @return 成功返回 true,失败返回 false
 */
-(bool)stopCamera;

# 切换摄像头

/**
 *  @breif 切换前后摄像头,内部会根据当前摄像头类型来进行切换
 *  @return 成功返回 true,失败返回 false
 */
-(bool)switchCamera;

示例代码

// 打开摄像头
[mediaDevice startCamera];

// 关闭摄像头
[mediaDevice stopCamera];

// 切换摄像头
[mediaDevice switchCamera];