录制
本章节着重介绍实时音视频有关录制的内容。网易云信实时音视频提供服务端录制和客户端录制两种录制方式。
服务端录制需要您联系我们的商务经理,开通此功能,并且需要在管理中心中成功配置消息抄送服务,假设A和B进行点对点实时音视频通话,服务端录制下来的文件包括:A的音视频文件(.mp4)B的音视频文件(.mp4)。如果是纯音频通话,则没有相应的音视频文件,如果您开通了服务端混合录制,则还会生成一个A和B的混合音视频文件(.mp4)。假设A、B、C进行多人实时音视频通话,进入房间时,设置了服务器录制音频开关、录制视频开关、单人文件录制开关、服务端录制下来的文件包括:A的音视频文件(.mp4)、B的音视频文件(.mp4)、C的音视频文件(.mp4)、一个A、B和C的混合音视频文件(.mp4),如果是纯音频通话,则没有相应的音视频文件。这些录制文件的地址在音视频通话结束之后会通过抄送服务抄送到您的服务器。
客户端录制支持录制单个通话者的音视频文件和录制通话的混合录音。
服务端录制
服务端录制客户端配置
介绍
服务端录制支持点对点及多人音视频服务端录制,视频合图布局提供画廊布局、以及主讲人布局两种模式,默认提供画廊布局,主讲人布局目前为使用版本,在开启通话或加入房间时需要在扩展参数中设置相关参数。
参数说明
键常量名 | 键常量值 | 值类型 | 说明 |
---|---|---|---|
kNIMVChatRecord | record | int | 是否需要服务器录制音频数据 >0表示是,默认不填为0 (需要服务器配置支持,本地录制直接调用接口函数) |
kNIMVChatVideoRecord | video_record | int | 是否需要服务器录制视频数据 >0表示是 ,默认不填为0(需要服务器配置支持,本地录制直接调用接口函数) |
kNIMVChatRecordType | record_type | int | 服务器录制模式NIMVChatServRecordType,默认为0 (需要服务器配置支持,并且开kNIMVChatRecord,kNIMVChatVideoRecord其中一个) |
kNIMVChatRHostSpeaker | r_host_speaker | int | 服务器混录时指定本人主画面,默认为0, 非0表示打开(需要服务器配置支持,及录制模式打开混录,并且为多人中的第一个主画面配置) |
画廊模式布局
视频录制布局目前提供默认的画廊模式,即等分模式,客户开通了录制功能,默认提供画廊模式。
- 当音视频房间参与视频合图人数为1人时,默认整个画面
- 当音视频房间参与视频合图人数为2人时,采用二宫格模式
- 当音视频房间参与视频合图人数为3或者4人时,采用四宫格模式
- 当音视频房间参与视频合图人数大于4人且小于等于9人时,采用九宫格模式
- 当音视频房间参与视频合图人数大于9人时,默认取前9个人视频画面合图
主讲人模式布局
主讲人模式布局目前为使用版本,客户开通了录制功能,需要在开启通话或加入房间时,在扩展Json参数中设置kNIMVChatRecordType和kNIMVChatRHostSpeaker,所谓主讲人模式即是视频合图画面中,一个成员视频画面作为背景突出显示。
示例
在线教育小班场景,客户希望录制整个课堂的混合文件,并且希望录制老师单人的文件。那么客户需要打开老师以及学生A、B的混合录制开关,以及打开老师单人录制开关,才可以生成一堂课混频文件、以及老师单人的视频文件
描述 | 多人录制(销售后台) | 音频混合录制(客户端) | 视频混合录制(客户端) | 录制模式 |
---|---|---|---|---|
开关设置 | 开 | 开 | 开 | 老师:混合+单人录制,学生:混合录制 |
生成文件 | / | / | 混屏mp4文件x1 | 视频mp4x1 |
服务端录制
服务器录制需要开通此功能,并且在本地发起时打开服务器录制开关。
服务器录制信息回调
由云信服务器抄送给用户的应用服务器。
客户端录制
音视频录制
开始录制
开始本地录制mp4文件,允许录制自己或其他单个成员的mp4文件。实际文件开始录制要等录制状态的回调通知。
API原型
C++
static void StartRecord(const std::string& path, const std::string& uid, Mp4OptCallback cb)
File:nim_cpp_vchat.h
Namespace:NIM
Class:VChat
C#
public static void StartRecord(string path, NIMVChatMP4RecordJsonEx recordInfo, nim_vchat_mp4_record_opt_cb_func cb)
Namespace:NIM
Class:VChatAPI
C
void nim_vchat_start_record(const char *path, const char *json_extension, nim_vchat_mp4_record_opt_cb_func cb, const void *user_data)
File:nim_vchat.h
参数说明
参数 | 类型 | 说明 |
---|---|---|
path | string | 录制文件路径 |
recordInfo | Json | 录制信息 NIMVChatMP4RecordJsonEx |
json_extension | string | Json string 扩展,kNIMVChatUid录制的成员,如果是自己填空(录制时允许同时混音对端声音,需要填kNIMVChatMp4AudioType),kNIMVChatMp4Recode标识是否重编码(默认打开,打开后kNIMVChatMp4Width和kNIMVChatMp4Height有效[16-1280有效]) |
cb | function | 结果回调 |
user_data | void* | APP的自定义用户数据,SDK只负责传回给回调函数cb,不做任何处理! |
返回值说明
无返回值。
回调返回错误码:NIMVChatMp4RecordCode,定义如下:
枚举定义 | 值 | 说明 |
---|---|---|
kNIMVChatMp4RecordClose | 0 | MP4结束 |
kNIMVChatMp4RecordVideoSizeError | 1 | MP4结束,视频画面大小变化 |
kNIMVChatMp4RecordOutDiskSpace | 2 | MP4结束,磁盘空间不足 |
kNIMVChatMp4RecordThreadBusy | 3 | MP4结束,录制线程繁忙 |
kNIMVChatMp4RecordCreate | 200 | MP4文件创建 |
kNIMVChatMp4RecordExsit | 400 | MP4文件已经存在 |
kNIMVChatMp4RecordCreateError | 403 | MP4文件创建失败 |
kNIMVChatMp4RecordInvalid | 404 | 通话不存在 |
示例
C++
void StartRecordCb(bool ret, int code, const std::string& file, __int64 time) { if (ret) { } else { } } void foo() { nim::VChat::StartRecord(path, uid, &StartRecordCb); }
C#
void VChatRecordStartCallback(bool ret, int code,string file,Int64 time,string json_extension,IntPtr user_data) { if(ret) { MessageBox.Show("开始录制"); } else { MessageBox.Show("录制失败-错误码:" + code.ToString()); } } void foo() { NIMVChatMP4RecordJsonEx recordInfo; recordInfo.RecordUid = uid;//如果录制自己填空 recordInfo.RecordPeopleType = 0;//如果需要所有成员包括自己的混音声音,填1 NIM.VChatAPI.StartRecord(path, recordInfo, VChatRecordStartCallback); }
C
void mp4_record_opt_cb(bool ret, int code, const char *file, __int64 time, const char *json_extension, const void *user_data) { } typedef void(*nim_vchat_start_record)(const char *path, const char *json_extension, nim_vchat_mp4_record_opt_cb_func cb, const void *user_data); void foo() { nim_vchat_start_record func = (nim_vchat_start_record) GetProcAddress(hInst, "nim_vchat_start_record"); Json::FastWriter fs; Json::Value value; value[nim::kNIMVChatUid] = uid;//如果录制自己填空 value[nim::kNIMVChatMp4AudioType] = 0;//如果需要所有成员包括自己的混音声音,填1 value[nim::kNIMVChatMp4Recode] = true;//Mp4 录制重编码开关打开 value[nim::kNIMVChatMp4Width] = 640; value[nim::kNIMVChatMp4Height] = 480; std::string json_value = fs.write(value); func(path.c_str(), json_value.c_str(), &mp4_record_opt_cb, nullptr); }
结束录制
主动结束本地录制mp4文件。
API原型
C++
static void StopRecord(const std::string& uid, Mp4OptCallback cb)
File:nim_cpp_vchat.h
Namespace:NIM
Class:VChat
C#
public static void StopRecord(NIMVChatMP4RecordJsonEx recordInfo, nim_vchat_mp4_record_opt_cb_func cb)
Namespace:NIM
Class:VChatAPI
C
void nim_vchat_stop_record(const char *json_extension, nim_vchat_mp4_record_opt_cb_func cb, const void *user_data)
File:nim_vchat.h
参数说明
参数 | 类型 | 说明 |
---|---|---|
recordInfo | Json | 录制信息 NIMVChatMP4RecordJsonEx |
json_extension | string | Json string 扩展,kNIMVChatUid录制的成员,如果是自己填空 |
cb | function | 结果回调 |
user_data | void* | APP的自定义用户数据,SDK只负责传回给回调函数cb,不做任何处理! |
返回值说明
无返回值。
回调返回错误码:NIMVChatMp4RecordCode
示例
C++
void StopRecordCb(bool ret, int code, const std::string& file, __int64 time) { if (ret) { } else { } } void foo() { nim::VChat::StopRecord(uid, &StopRecordCb); }
C#
void VChatRecordStopCallback(bool ret, int code,string file,Int64 time,string json_extension,IntPtr user_data) { if(ret) { MessageBox.Show("结束录制"); } else { MessageBox.Show("录制失败-错误码:" + code.ToString()); } } void foo() { NIMVChatMP4RecordJsonEx recordInfo; recordInfo.RecordUid = uid;//如果录制自己填空 recordInfo.RecordRecode = false;//关闭mp4录制重编码 NIM.VChatAPI.StopRecord(recordInfo, VChatRecordStopCallback); }
C
void mp4_record_opt_cb(bool ret, int code, const char *file, __int64 time, const char *json_extension, const void *user_data) { } typedef void(*nim_vchat_stop_record)(const char *json_extension, nim_vchat_mp4_record_opt_cb_func cb, const void *user_data); void foo() { nim_vchat_stop_record func = (nim_vchat_stop_record) GetProcAddress(hInst, "nim_vchat_stop_record"); Json::FastWriter fs; Json::Value value; value[nim::kNIMVChatUid] = uid;//如果录制自己填空 std::string json_value = fs.write(value); func(json_value.c_str(), &mp4_record_opt_cb, nullptr); }
录制相关回调
当开始MP4录制时,等有实际数据开始录制会回调录制开始的通知,等录制结束或录制异常会有录制结束的通知。
API原型
C#
NIMVChatSessionStatus.onSessionMp4InfoStateNotify
public delegate void OnSessionMP4InfoNotifyHandler(long channel_id, int code, NIMVChatMP4State mp4_info)
Namespace:NIM
Class:VChatAPI
C++/C
typedef void (*nim_vchat_cb_func)(enum NIMVideoChatSessionType type, int64 channel_id, int code, const char *json_extension, const void *user_data)
NIMVideoChatSessionType::kNIMVideoChatSessionTypeMp4Notify
File:nim_vchat_def.h
参数说明
- C#
参数 | 类型 | 说明 |
---|---|---|
channel_id | long | 频道id |
code | int | 无效错误码 |
mp4_info | object | 录制回调信息 |
- C++/C
参数 | 类型 | 说明 |
---|---|---|
type | enum | 回调类型kNIMVideoChatSessionTypeMp4Notify |
channel_id | int64 | 频道id |
code | int | 无效错误码 |
json_extension | string | Json string 扩展,返回kNIMVChatMp4Start或kNIMVChatMp4Close |
user_data | void* | 注册回调时的APP自定义用户数据,SDK只负责传回给回调函数cb,不做任何处理 |
示例
C#
private static void OnSessionMp4InfoStateNotify(long channel_id, int code, NIMVChatMP4State mp4_info) { string log = ""; if(mp4_info.MP4_Close!=null) { log += "close: file_path:" + mp4_info.MP4_Close.FilePath + " duration:" + mp4_info.MP4_Close.Duration; } if(mp4_info.MP4_Start!=null) { log += "start: file_path:" + mp4_info.MP4_Start.FilePath + " duration:" + mp4_info.MP4_Start.Duration; } DemoTrace.WriteLine("SessionMp4Info " + log); }
C++/C
void VChatCb(nim::NIMVideoChatSessionType type, __int64 channel_id, int code, const char *json, const void*) { switch (type) { case nim::kNIMVideoChatSessionTypeMp4Notify:{ Json::Value valus; Json::Reader reader; if (reader.parse(json, valus)) { if (valus[nim::kNIMVChatMp4Start].isObject()) { std::string path = valus[nim::kNIMVChatMp4Start][nim::kNIMVChatMp4File].asString(); int64_t time = valus[nim::kNIMVChatMp4Start][nim::kNIMVChatTime].asInt64(); //录制开始 } else if (valus[nim::kNIMVChatMp4Close].isObject()) { std::string path = valus[nim::kNIMVChatMp4Close][nim::kNIMVChatMp4File].asString(); int code = valus[nim::kNIMVChatMp4Close][nim::kNIMVChatStatus].asInt(); int64_t time = valus[nim::kNIMVChatMp4Close][nim::kNIMVChatTime].asInt64(); //录制结束,code非200为异常错误,见NIMVChatMp4RecordCode } } }break; //··· } }
通话录音
开始通话录音
开始本地录制混音文件,将录制通话中所有成员声音。
API原型
C++
static void StartAudioRecord(const std::string& path, AudioRecordCallback cb)
File:nim_cpp_vchat.h
Namespace:NIM
Class:VChat
C#
public static void StartAudioRecord(string path, nim_vchat_audio_record_opt_cb_func cb)
Namespace:NIM
Class:VChatAPI
C
void nim_vchat_start_audio_record(const char *path, const char *json_extension, nim_vchat_audio_record_opt_cb_func cb, const void *user_data)
File:nim_vchat.h
参数说明
参数 | 类型 | 说明 |
---|---|---|
path | string | 录制文件路径 |
json_extension | string | Json string 无效扩展 |
cb | function | 结果回调 |
user_data | void* | APP的自定义用户数据,SDK只负责传回给回调函数cb,不做任何处理! |
返回值说明
无返回值。
回调返回错误码:NIMVChatAudioRecordCode,定义如下:
枚举定义 | 值 | 说明 |
---|---|---|
kNIMVChatAudioRecordClose | 0 | 录制正常结束 |
kNIMVChatAudioRecordOutDiskSpace | 2 | 录制结束,磁盘空间不足 |
kNIMVChatAudioRecordCreate | 200 | 文件创建成功 |
kNIMVChatAudioRecordExsit | 400 | 已经存在 |
kNIMVChatAudioRecordCreateError | 403 | 文件创建失败 |
kNIMVChatAudioRecordInvalid | 404 | 通话不存在 |
示例
C++
void StartRecordCb(bool ret, int code, const std::string& file, __int64 time) { if (ret) { } else { } } void foo() { nim::VChat::StartAudioRecord(path, &StartRecordCb); }
C#
void VChatRecordStartCallback(bool ret, int code,string file,Int64 time,string json_extension,IntPtr user_data) { if(ret) { MessageBox.Show("开始录制"); } else { MessageBox.Show("录制失败-错误码:" + code.ToString()); } } void foo() { NIM.VChatAPI.StartAudioRecord(path, VChatRecordStartCallback); }
C
void audio_record_opt_cb(bool ret, int code, const char *file, __int64 time, const char *json_extension, const void *user_data) { } typedef void(*nim_vchat_start_audio_record)(const char *path, const char *json_extension, nim_vchat_audio_record_opt_cb_func cb, const void *user_data); void foo() { nim_vchat_start_audio_record func = (nim_vchat_start_audio_record) GetProcAddress(hInst, "nim_vchat_start_audio_record"); func(path.c_str(), "", &mp4_record_opt_cb, nullptr); }
结束通话录音
主动结束本地录制音频文件。
API原型
C++
static void StopAudioRecord(AudioRecordCallback cb)
File:nim_cpp_vchat.h
Namespace:NIM
Class:VChat
C#
public static void StopAudioRecord(nim_vchat_audio_record_opt_cb_func cb)
Namespace:NIM
Class:VChatAPI
C
void nim_vchat_stop_audio_record(const char *json_extension, nim_vchat_audio_record_opt_cb_func cb, const void *user_data)
File:nim_vchat.h
参数说明
参数 | 类型 | 说明 |
---|---|---|
json_extension | string | Json string 无效扩展 |
cb | function | 结果回调 |
user_data | void* | APP的自定义用户数据,SDK只负责传回给回调函数cb,不做任何处理! |
返回值说明
无返回值。
回调返回错误码:NIMVChatAudioRecordCode
示例
C++
void StopRecordCb(bool ret, int code, const std::string& file, __int64 time) { if (ret) { } else { } } void foo() { nim::VChat::StopAudioRecord(&StopRecordCb); }
C#
void VChatRecordStopCallback(bool ret, int code,string file,Int64 time,string json_extension,IntPtr user_data) { if(ret) { MessageBox.Show("结束录制"); } else { MessageBox.Show("录制失败-错误码:" + code.ToString()); } } void foo() { NIM.VChatAPI.StopAudioRecord(VChatRecordStopCallback); }
C
void audio_record_opt_cb(bool ret, int code, const char *file, __int64 time, const char *json_extension, const void *user_data) { } typedef void(*nim_vchat_stop_audio_record)(const char *json_extension, nim_vchat_audio_record_opt_cb_func cb, const void *user_data); void foo() { nim_vchat_stop_audio_record func = (nim_vchat_stop_audio_record) GetProcAddress(hInst, "nim_vchat_stop_audio_record"); func("", &audio_record_opt_cb, nullptr); }
录制相关回调
当开始混音录制时,等有实际数据开始录制会回调录制开始的通知,等录制结束或录制异常会有录制结束的通知。
API原型
C#
NIMVChatSessionStatus.onSessionAuRecordInfoStateNotify
public delegate void OnSessionAuRecordInfoNotifyHandler(long channel_id, int code, NIMVChatAuRecordState record_info)
Namespace:NIM
Class:VChatAPI
C++/C
typedef void (*nim_vchat_cb_func)(enum NIMVideoChatSessionType type, int64 channel_id, int code, const char *json_extension, const void *user_data)
NIMVideoChatSessionType::kNIMVideoChatSessionTypeAuRecordNotify
File:nim_vchat_def.h
参数说明
- C#
参数 | 类型 | 说明 |
---|---|---|
channel_id | long | 频道id |
code | int | 无效错误码 |
record_info | object | 录制回调信息 |
- C++/C
参数 | 类型 | 说明 |
---|---|---|
type | enum | 回调类型kNIMVideoChatSessionTypeAuRecordNotify |
channel_id | int64 | 频道id |
code | int | 无效错误码 |
json_extension | string | Json string 扩展,返回kNIMVChatAuRecordStart或kNIMVChatAuRecordClose |
user_data | void* | 注册回调时的APP自定义用户数据,SDK只负责传回给回调函数cb,不做任何处理 |
示例
C#
private static void OnSessionAuRecordInfoStateNotify(long channel_id, int code, NIMVChatAuRecordState record_info) { string log = ""; if(record_info.AudioRecordClose!=null) { log += "close: file_path:" + record_info.AudioRecordClose.FilePath + " duration:" + record_info.AudioRecordClose.duration; } if(record_info.AudioRecordStart!=null) { log += "start: file_path:" + record_info.AudioRecordStart.FilePath + " duration:" + record_info.AudioRecordStart.duration; } DemoTrace.WriteLine("SessionAuRecordInfo " + log); }
C++/C
void VChatCb(nim::NIMVideoChatSessionType type, __int64 channel_id, int code, const char *json, const void*) { switch (type) { case nim::kNIMVideoChatSessionTypeAuRecordNotify:{ Json::Value valus; Json::Reader reader; if (reader.parse(json, valus)) { if (valus[nim::kNIMVChatMp4Start].isObject()) { std::string path = valus[nim::kNIMVChatAuRecordStart][nim::kNIMVChatFile].asString(); int64_t time = valus[nim::kNIMVChatAuRecordStart][nim::kNIMVChatTime].asInt64(); //录制开始 } else if (valus[nim::kNIMVChatAuRecordClose].isObject()) { std::string path = valus[nim::kNIMVChatAuRecordClose][nim::kNIMVChatFile].asString(); int code = valus[nim::kNIMVChatAuRecordClose][nim::kNIMVChatStatus].asInt(); int64_t time = valus[nim::kNIMVChatAuRecordClose][nim::kNIMVChatTime].asInt64(); //录制结束,code非200为异常错误,见NIMVChatAudioRecordCode } } }break; //··· } }