# 视频管理

# 视频数据采集管理

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

视频采集前,可以指定要开启的摄像头

首先,获取摄像头列表

/// <summary>
/// 摄像头列表
/// </summary>
public List<JCMediaDeviceCamera> cameras

其中,JCMediaDeviceCamera 有以下几个变量

/// <summary>
/// 名称
/// </summary>
public string cameraName { get; internal set; }
/// <summary>
/// id
/// </summary>
public string cameraId

切换摄像头

/// <summary>
/// 切换摄像头
/// </summary>
/// <param name="camera">要切换的摄像头</param>
/// <returns>true为切换成功,false为切换失败</returns>
public bool switchCamera(JCMediaDeviceCamera camera)

示例代码

// 获取摄像头列表
List<JCMediaDeviceCamera> cameraDevices = mediaDevice.cameraDevices;

// 切换摄像头
mediaDevice.switchCamera(mediaDevice.cameras[0]);

# 设置摄像头采集分辨率

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

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

/// <summary>
/// 设定摄像头分辨率,请在调用startCamera()接口之前调用才会生效
/// </summary>
/// <param name="width">摄像头分辨率宽</param>
/// <param name="height">摄像头分辨率高</param>
/// <param name="framerate">帧速率</param>
public void setCameraProperty(int width, int height, int framerate)

示例代码

// 设置摄像头采集属性
mediaDevice.setCameraProperty(640, 360, 30);

# 自定义视频输入源

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

  1. 调用 startVideoFile (opens new window) 开启该功能。(调用 stopVideoFile (opens new window) 关闭该功能)
  2. 调用 setVideoFileFrame (opens new window) 逐帧添加视频数据。
  3. 进行 视频画面渲染
// 开启以视频文件作为视频源的功能
mediaDevice.startVideoFile();
// 添加视频数据源
mediaDevice.setVideoFileFrame(data,JCMediaDevice.I420, "width","height","angle","mirror", "keyFrame");
// 渲染画面
...

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 界面显示,该接口会打开摄像头

/// <summary>
/// 获取预览视频对象,通过此对象能获得视图用于UI显示
/// </summary>
/// <param name="mode">渲染方式</param>
/// <returns>JCMediaDeviceVideoCanvas对象</returns>
public JCMediaDeviceVideoCanvas startCameraVideo(JCMediaDeviceRenderMode mode)

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

名称

描述

FULLSCREEN

铺满窗口

FULLCONTENT

全图像显示,会有黑边,但在窗口跟图像比例相同的情况下不会有黑边

AUTO

自适应

  • 远端视频渲染

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

/// <summary>
/// 获得视频对象,通过此对象能获得视图用于UI显示
/// </summary>
/// <param name="videoSource">渲染标识串,比如 JCMediaChannelParticipant JCCallItem 中的 renderId,当videoSource 为 videoFileId 时,内部会调用 startVideoFile</param>
/// <param name="mode">渲染模式</param>
/// <returns>JCMediaDeviceVideoCanvas对象</returns>
public JCMediaDeviceVideoCanvas startVideo(string videoSource, JCMediaDeviceRenderMode mode)

示例代码

// 获取摄像头列表
List<JCMediaDeviceCamera> cameraDevices = mediaDevice.cameras;

// 打开本地视频预览
JCMediaDeviceVideoCanvas localCanvas = mediaDevice.startCameraVideo(JCMediaDeviceRenderMode.FULLCONTENT);
ImageBrush image = new ImageBrush(localCanvas.videoView);
image.Stretch = Stretch.Uniform;
this.label.Background = image;

// 远端视频渲染,renderId来源于通话对象,一对一为JCCallItem对象,多方为JCMediaChannelParticipant对象
JCMediaDeviceVideoCanvas remoteCanvas = mediaDevice.startVideo(renderId, JCMediaDeviceRenderMode.FULLSCREEN);
ImageBrush image = new ImageBrush(remoteCanvas.videoView);
image.Stretch = Stretch.Uniform;
this.label.Background = image;

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

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

/// <summary>
/// 停止视频
/// </summary>
/// <param name="canvas">JCMediaDeviceVideoCanvas对象,由startVideo获得</param>
public void stopVideo(JCMediaDeviceVideoCanvas canvas)

示例代码

JCMediaDeviceVideoCanvas localCanvas = mediaDevice.startCameraVideo(JCMediaDeviceRenderMode.FULLCONTENT);
JCMediaDeviceVideoCanvas remoteCanvas = mediaDevice.startVideo(renderId, JCMediaDeviceRenderMode.FULLSCREEN);
if (localCanvas != null)
    {
        this.smvideoGrid.Background = null;
        mediaDevice.stopVideo(localCanvas);
        localCanvas = null;
    }
if (remoteCanvas != null)
    {
        this.fullvideoGrid.Background = null;
        mediaDevice.stopVideo(remoteCanvas);
        remoteCanvas = null;
    }

# 视频通话截图

/// <summary>
/// 视频通话截图
/// </summary>
/// <param name="width">截屏宽度像素,-1为视频源像素</param>
/// <param name="height">截屏高度像素,-1为视频源像素</param>
/// <param name="filePath">文件路径</param>
/// <returns>是否成功</returns>
public bool snapshot(int width, int height, string filePath)

# 更新视频渲染标识

如果想替换当前摄像头视频画面,可以调用下面的接口

/// <summary>
/// 更新视频渲染标识
/// </summary>
/// <param name="videoSource">渲染标识</param>
/// <returns>成功失败</returns>
public bool replace(string videoSource)

# 暂停渲染

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

/// <summary>
/// 暂停渲染
/// </summary>
/// <returns>成功失败</returns>
public bool pause()

# 恢复渲染

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

/// <summary>
/// 恢复渲染
/// </summary>
/// <returns>成功失败</returns>
public bool resume()

# 解决视频渲染黑屏问题

如果在集成过程中遇到视频黑屏的问题,您可以通过软件渲染的方式解决。通过以下几个步骤实现软件渲染:

  1. 调用 JCMediaDevice 对象中的 startCamera 开启自身摄像头。
  2. 创建 ZmfBitmap 对象,打开自身的视频渲染。
  3. 调用 JCMediaChannel 对象中的 requestVideo 请求其他成员的视频流。(参数中设置开启)
  4. 创建 ZmfBitmap 对象,打开其他成员的视频渲染。

在您不需要软件渲染的时候,通过以下几个步骤关闭软件渲染:

  1. 当不需要渲染时,调用 ZmfBitmap 对象的 Dispose 方法停止渲染,并置空 ZmfBitmap 对象。
  2. 调用 JCMediaDevice 对象中 stopCamera 关闭自身摄像头。
  3. 调用 JCMediaChannel 对象中的 requestVideo 关闭其他成员的视频流。(参数中设置关闭)
开启软件渲染示例代码
// 1. 打开自身摄像头
mediaDevice.startCamera();
// 2. 创建 ZmfBitmap 对象,打开自身的视频渲染
ZmfBitmap _callLocalZmfBitmap = new ZmfBitmap(mediaDevice.camera.cameraId,
        (string renderId, ImageSource source, int width, int height, int angle) =>
        {
            if (source != null)
            {	
              // 通过 source 构建 ImageBrush 对象设置画面
              ImageBrush brush = new ImageBrush(source);
              brush.Stretch = Stretch.UniformToFill;
              viewCallLocalVideo.Background = brush;
              // 开启自身视频渲染,可以选择开启镜像,同时设置视频画面的初始方向
              TransformGroup group = new TransformGroup();
              ScaleTransform scaleTransform = new ScaleTransform();
              scaleTransform.ScaleX = -1;
              group.Children.Add(scaleTransform);
              group.Children.Add(new RotateTransform(angle));
              viewCallLocalVideo.LayoutTransform = group;
            }
        }, (string renderId, ImageSource source, int width, int height, int angle) =>{
          // windows 下开启自身视频渲染不会收到此回调
        });

// 3. 请求其他成员的视频流
mediaChannel.requestScreenVideo(mediaChannel.screenRenderId,                                 (JCMediaChannelPictureSize)Properties.Settings.Default.ConfPictureSize);
// 4. 创建 ZmfBitmap 对象,打开其他成员的视频渲染
ZmfBitmap _confOtherZmfBitmap_others = new ZmfBitmap(jCMediaChannelParticipant.renderId,
        (string renderId, ImageSource source, int width, int height, int angle) =>
        {
            if (source != null)
            {
                // 通过 source 构建 ImageBrush 对象设置画面
                ImageBrush brush = new ImageBrush(source);
                brush.Stretch = getStretchByRenderMode();
                viewConfOtherVideo.Background = brush;
                viewConfOtherVideo.LayoutTransform = new RotateTransform(angle);
            }
        }, (string renderId, ImageSource source, int width, int height, int angle) =>
        {
            // 处理视频旋转
            viewConfOtherVideo.LayoutTransform = new RotateTransform(angle);
        });
关闭软件渲染的示例代码
// 1. 调用 ZmfBitmap 对象的 Dispose 方法停止渲染,并置空 ZmfBitmap 对象。
_callLocalZmfBitmap.Dispose(); 
_callLocalZmfBitmap = null;

_confOtherZmfBitmap_others.Dispose();
_confOtherZmfBitmap_others = null;

// 2. 关闭自身摄像头
mediaDevice.stopCamera();

// 3. 关闭其他成员的视频流
mediaChannel.requestVideo(jCMediaChannelParticipant, JCMediaChannelPictureSize.None);
相关 API 介绍
// 构造方法。renderId 为摄像头id或者视频流,ResizeHandler 为视频渲染大小变化的接口回调,rotate 为视频渲染角度变化的接口回调
public ZmfBitmap(string renderId, ResizeHandler resize, RotateHandler rotate);
// 停止渲染
public void Dispose();
// 视频渲染大小变化的接口回调
public delegate void ResizeHandler(string renderId, ImageSource srouce, int width, int height, int angle);
// 视频渲染角度变化的接口回调
public delegate void RotateHandler(string renderId, ImageSource srouce, int width, int height, int angle);

# 视频设备管理

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

# 获取当前使用摄像头和默认摄像头

/// <summary>
/// 当前使用摄像头
/// </summary>
public JCMediaDeviceCamera camera

/// <summary>
/// 默认摄像头
/// </summary>
public JCMediaDeviceCamera defaultCamera

# 开启关闭摄像头

/// <summary>
/// 开启摄像头
/// </summary>
/// <returns>true为开启成功,false为开启失败</returns>
public bool startCamera()

/// <summary>
/// 关闭摄像头
/// </summary>
/// <returns>true为关闭成功,false为关闭失败</returns>
public bool stopCamera()

# 切换摄像头

/// <summary>
/// 切换摄像头
/// </summary>
/// <param name="camera">要切换的摄像头</param>
/// <returns>true为切换成功,false为切换失败</returns>
public bool switchCamera(JCMediaDeviceCamera camera)

示例代码

// 打开摄像头
mediaDevice.startCamera();

// 关闭摄像头
mediaDevice.stopCamera();

// 切换摄像头
mediaDevice.switchCamera(mediaDevice.cameras[0]);