Appearance
当前版本:V1.1.5
©苏州必捷网络有限公司
1 概述
1.1 目的
用于指导用户使用BJSDK开发国产操作系统发送端应用程序。
1.2 读者对象
本文档适用于国产操作系统发送端应用程序的开发人员和测试人员。
1.3 缩略语定义
缩写名称 | 英文 | 中文 |
---|---|---|
BJCast | BJCast | 必捷无线投屏协议 |
2 范围
2.1 功能
本SDK为Linux发射端BJSDK,和BJCast接收端SDK配合使用进行无线投屏。
本SDK libbjcast_project.so 和 libbjcast_sender.so 等so库为标准的动态库,配套 bjcast_project_lib.h 头文件,提供API接口供应用集成。
2.2 总体框架
Linux发射端【必捷投屏】总体框架分为三层:
SDK层:libbjcast_project.so 和 **libbjcast_sender.so ** 等so库为动态库。实现了投屏控制协议、录屏功能、媒体传输和处理协议。头文件 bjcast_project_lib.h 提供C接口供外部应用集成。
addon层:我司使用addon集成调用SDK里面的方法,封装后提供addon接口供应用层调用。
(PS:客户可按需求使用其他方式集成调用SDK里面的方法)
应用层:我司使用addon提供的接口完成应用层前端界面相应功能的实现。
2.3 SDK交付物
- SDK库文件
- SDK接口文档
3 API接口
3.1 初始化SDK接口
C
int32_t BJCastLibInit(const BJCastSenderLibInitPara *initPara);
描述:
初始化SDK,App在启动时作初始化调用。
参数:
BJCastSenderLibInitPara: SDK初始化参数结构体,包含以下参数:
参数名 | 描述 | 必填 |
---|---|---|
BJCastSenderLibInfListener | SDK初始化回调接口结构体,由应用层实现 | 是 |
username | 用户名 | 是 |
sender_id | 发射端的UUID,由应用层设定生成,必须保证唯一,接收端根据这个值来判断是不是重连 | 是 |
mac_addr | Mac地址,如果不需要则初始化为"" | 否 |
custom_info | 用户投屏时携带的扩展字段,最大长度256字节,如果不需要该功能,设为"" | 否 |
encode_scene_conf | 编码器场景参数配置,参考典型配置文件,如果不需设置,请设为"" | 否 |
encoder_ext_conf | 编码器的额外参数设置,参考ffmpeg | 否 |
channel_id | SDK渠道ID,由BJ提供 | 是 |
channel_code | SDK渠道Code,由BJ提供 | 是 |
log_directory | 日志配置文件路径,由用户设定,如果不需要该功能初始化为"" | 否 |
BJCastSenderLibInfListener: SDK初始化回调接口结构体,包含以下回调方法设置:
参数名 | 描述 | 必填 |
---|---|---|
*void (OnSessionEnd)(int reason) = 0 | 会话结束回调接口, 返回一些投屏错误 | 是 |
*void (OnSessionNotify)(int) = 0 | 会话提示回调接口,返回一些投屏会话提醒 | 是 |
*int (OnSessionHeartbeatLost)(int hb_loss_count) = 0 | 心跳丢失回调接口,返回值: 0: 保持投屏 1: 结束投屏 | 是 |
*void (OnSessionHeartbeatRecovered)() = 0 | 心跳检测回调,持续监测投屏状态 | 是 |
*void (OnStartSessionResult)(int reason) = 0 | 投屏返回结果回调,通知应用程序投屏成功或者失败 | 是 |
返回值:
返回值 | 含义 |
---|---|
0 | 初始化成功 |
-1 | 初始化失败 |
-101 | 初始化SDK channel信息失败,比如 channel_id 或 channel_code 填写错误 |
参考用例: <初始化SDK函数>
C/C++代码:
C
// 控制pin码输入
static bool inputpin = false;
// 回调函数实现类
struct TestBJCastSenderLibInfListenerImp
{
static void OnSessionEnd(int error)
{
TRACE_DBG("OnBjcastSessionEnd error:%d ", error);
}
static void OnSessionNotify(int status)
{
TRACE_DBG("OnSessionNotify status:%d ", status);
}
static int OnSessionHeartbeatLost(int hb_loss_count)
{
TRACE_WARN("OnBJCastSessionHeartbeatLost hb_loss_count:%d ", hb_loss_count);
return 0;
}
static void OnSessionHeartbeatRecovered()
{
TRACE_WARN("OnSessionHeartbeatRecovered");
}
static void OnStartSessionResult(int code)
{
std::cout << std::endl;
TRACE_DBG("OnStartSessionResult code:%d", code);
if (code == E_AUTH_FAILED)
{
inputpin = true;
}
}
};
// 初始化SDK函数,只有 返回值为0 时才能进行后面的操作!
int main(int argc, char *argv[])
{
// 日志配置文件路径
std::string logConfigPath = GetLogConfigFilePath();
TRACE_INFO("Main::Current logConfigPath:%s", logConfigPath.c_str());
// 设置日志显示类型: 0:将日志信息写入日志文件 1:打印日志信息到控制台
int log_display_type = 1;
BJCastSetLogDisplayType(log_display_type);
InitializeLogging(logConfigPath);
// sceneConf文件路径
std::string encodeScenefilePath = GetEncodeSceneConfigFilePath();
TRACE_INFO("Main::Current encodeScenefilePath:%s", encodeScenefilePath.c_str());
std::string sender_id = GetNowTime() + "sender456";
BJCastSenderLibInitPara para;
memset(¶, 0, sizeof(para));
para.channel_id = ; // SDK渠道id, 由BJ提供
para.channel_code = ""; // SDK渠道code, 由BJ提供
para.user_name = "test";
para.sender_id = sender_id.c_str(); // UUID, 由应用层设定生成, 必须保证唯一
para.mac_addr = ""; // Mac地址
para.custom_info = "";
para.log_directory = logConfigPath.c_str(); // 日志路径地址
para.encode_scene_conf = encodeScenefilePath.c_str(); // 编码场景配置文件路径
para.listener.OnSessionEnd = &TestBJCastSenderLibInfListenerImp::OnSessionEnd;
para.listener.OnSessionNotify = &TestBJCastSenderLibInfListenerImp::OnSessionNotify;
para.listener.OnSessionHeartbeatLost = &TestBJCastSenderLibInfListenerImp::OnSessionHeartbeatLost;
para.listener.OnSessionHeartbeatRecovered = &TestBJCastSenderLibInfListenerImp::OnSessionHeartbeatRecovered;
para.listener.OnStartSessionResult = &TestBJCastSenderLibInfListenerImp::OnStartSessionResult;
int ret = 0;
ret = BJCastLibInit(¶); //调用初始化接口完成SDK的初始化
assert(ret == 0);
return 0;
}
log.properties 日志参考配置文件:
properties
log4cplus.logger.file_logger=DEBUG,logFile
log4cplus.additivity.file_logger=FALSE
log4cplus.appender.logFile=log4cplus::RollingFileAppender
log4cplus.appender.logFile.File=./logs/bj_cast.log
log4cplus.appender.logFile.UseLockFile=false
log4cplus.appender.logFile.MaxFileSize=512KB
log4cplus.appender.logFile.MaxBackupIndex=4
log4cplus.appender.logFile.CreateDirs=true
log4cplus.appender.logFile.ImmediateFlush=false
log4cplus.appender.logFile.Encoding=utf-8
log4cplus.appender.logFile.layout=log4cplus::PatternLayout
log4cplus.appender.logFile.layout.ConversionPattern=[%D{%Y/%m/%d %H:%M:%S,%Q} %-5p] - %m%n
3.2 获取屏幕个数接口
C
int32_t BJCastScreenCount();
描述:
获取物理屏幕个数,在一般情况下为1,如果有双显示屏,返回值为2。
**返回值:**屏幕个数。
**参考用例: ** <获取屏幕个数函数>,只有在正确初始化SDK函数后才能进行获取屏幕个数的操作!
C++
int main(int argc, char *argv[])
{
//......
int ret = 0;
ret = BJCastLibInit(¶);
assert(ret == 0);
// 只有在正确初始化SDK函数后才能进行获取屏幕个数的操作!!!
int count = BJCastScreenCount();
TRACE_INFO("Current monitor number is: %d", count);
return 0;
}
3.3 音频控制接口
c
int32_t BJCastAudioControl(BJCastProjectAudioCapPara *para);
描述:
SDK初始化后,投屏前可通过此接口对是否采集音频进行控制,默认不启用音频。
**参数: **
参数名 | 描述 | 必填 |
---|---|---|
BJCastProjectAudioCapPara | 音频控制结构体 | 是 |
包含以下字段:
参数名 | 描述 | 必填 |
---|---|---|
audio_type | 音频类型: 0: 关闭声音 1:开启声音 | 是 |
返回值:
返回值 | 含义 |
---|---|
0 | 设置成功 |
-1 | 参数非法 |
**参考用例: ** <音频控制函数>,只有在正确初始化SDK函数后才能进行音频控制操作!
C/C++代码:
c++
int main(int argc, char *argv[])
{
//......
int ret = 0;
ret = BJCastLibInit(¶);
assert(ret == 0);
// 只有在正确初始化SDK函数后才能进行音频控制操作!!!
// audio control
BJCastProjectAudioCapPara audio_para;
audio_para.audio_type = 1;
BJCastAudioControl(&audio_para);
return 0;
}
3.4 屏幕选择接口
C
int32_t BJCastScreenChoice(int32_t screen_type);
描述:
在外接拓展屏时,投屏屏幕显示选择,默认投屏主屏幕。
参数:
参数名 | 描述 | 必填 |
---|---|---|
screen_type | 屏幕类型: 0: 主屏幕 1: 拓展屏 | 是 |
返回值:
返回值 | 含义 |
---|---|
0 | 设置成功 |
-1 | 参数非法 |
3.5 编码选择接口
C
int32_t BJCastEncodeChoice(int32_t encode_type);
描述:
通过此接口设置编码类型,默认H264。
参数:
参数名 | 描述 | 必填 |
---|---|---|
encode_type | 编码类型: 0:优先H264 1:优先H265,若不支持H265,则为H264 | 是 |
返回值:
返回值 | 含义 |
---|---|
0 | 设置成功 |
-1 | 参数非法 |
3.6 开始投屏接口
C
int32_t BJCastStartCastSession(const BJCastProjectSessionPara *para);
描述:
通过此接口发起投屏,发起投屏的结果会通过 OnStartSessionResult 回调函数通知用户。
参数:
BJCastProjectSessionPara: 投屏会话参数结构体,包含以下参数:
参数名 | 描述 | 必填 |
---|---|---|
BJCastProjectReceiverPara | 接收端参数结构体 | 是 |
pin | 投屏密码,无密码则填空字符串"",注意不可填null | 是 |
username | 用户名 | 是 |
接收端参数结构体 BJCastProjectReceiverPara 包含以下参数:
参数名 | 描述 | 必填 |
---|---|---|
ip | 接收端IP地址 | 是 |
port | 接收端端口号 | 是 |
remote_ft | 接收端ft值 | 是 |
remote_max_resolution | 接收端支持的最大分辨率 | 是 |
**BJCastStartCastSession ** 投屏函数有以下返回值:
返回值 | 含义 |
---|---|
0 | 投屏成功 |
-1 | 投屏失败 |
-121 | SDK channel 信息错误 |
OnSessionEnd 回调函数有以下返回值:
返回值 | 含义 |
---|---|
2 | 接收端踢出投屏 |
0 | 退出投屏 |
-17 | 心跳丢失 |
OnStartSessionResult 回调函数有以下返回值:
返回值 | 含义 |
---|---|
0 | 投屏成功 |
-6 | 网络异常 |
-7 | 请求超时 |
-8 | 无效参数 |
-14 | 当前投屏被拒绝 |
-15 | 接收端投屏已满 |
-16 | PIN码错误或触发PIN码投屏事件 |
-51 | 媒体部分错误 |
OnSessionNotify 回调函数有以下返回值:
返回值 | 含义 |
---|---|
0 | 请求全屏成功 / 退出全屏成功 |
**参考用例: ** <开始投屏函数>,只有在正确初始化SDK函数后才能进行后面操作!
C/C++代码:
c
int main(int argc, char *argv[])
{
//......
int ret = 0;
ret = BJCastLibInit(¶);
assert(ret == 0);
//只有在正确初始化SDK函数后才能进行后面操作!!!
// 获取当前进程的进程 ID
pid_t pid = getpid();
TRACE_INFO("Current Process ID(PID): %d", pid);
extern char *__progname;
TRACE_INFO("program name: %s\n", __progname);
std::string password = "";
BJCastProjectSessionPara session_para;
memset(&session_para, 0, sizeof(session_para));
session_para.pin = password.c_str();
session_para.receiver.ip = argv[1];
session_para.receiver.port = 8190;
session_para.receiver.remote_ft = 31;
session_para.receiver.remote_max_resolution = 3;
session_para.username = "";
BJCastStartCastSession(&session_para);
#ifdef _WIN32
Sleep(10000); // _win32
#else
// usleep(10000000); // 10s
usleep(1000000000); // 1000s
BJCastStopCastSession();
#endif
#ifdef _WIN32
Sleep(1000); // _win32
#else
usleep(2000000); // 2s
#endif
return 0;
}
3.7 PIN码校验接口
C
int32_t BJCastCastSessionReAuth(const char *pass);
描述:
pin码校验,如果用户开启了pin码模式,发起投屏后会判断pin码是否正确,不正确会通过OnStartSessionResult回调函数返回 -16 无法正常投屏。
参数:
参数名 | 描述 | 必填 |
---|---|---|
pass | 用户输入的pin码 | 否 |
3.8 结束投屏接口
C
int32_t BJCastStopCastSession();
描述:
正在投屏或退出程序时,通过此接口结束投屏,会通过 OnSessionEnd 回调函数返回结果。
返回值:
返回值 | 含义 |
---|---|
0 | 成功结束投屏 |
3.9 请求全屏接口
C
int32_t BJCastRequestFullscreen();
描述:
只有在投屏状态,并且多路投屏时,才可通过此接口请求全屏控制,会通过OnSessionNotify回调函数返回结果。
返回值:
返回值 | 含义 |
---|---|
0 | 成功请求全屏 |
3.10 退出全屏接口
C
int32_t BJCastExitFullscreen();
描述:
只有在投屏状态,多路投屏并且处于全屏状态时,才可通过此接口退出全屏控制,会通过 OnSessionNotify 回调函数返回结果。
返回值:
返回值 | 含义 |
---|---|
0 | 成功退出全屏 |
3.11 获取SDK版本号接口
C
const char* BJCastGetCurrentSDKLibVerion();
描述:
获取当前SDK-Lib版本号。
返回值:
当前SDK-Lib版本号。
3.12 设置投屏质量场景配置接口
C
int32_t BJCastSetEncodeSceneConfig(const char *encode_scene_conf);
描述:
SDK初始化后,投屏前可通过此接口对投屏质量进行控制。
参数:
名称 | 描述 | 必填 |
---|---|---|
encode_scene_conf | json格式的投屏质量场景配置字符串 | 否 |
返回值:
返回值 | 含义 |
---|---|
0 | 设置成功 |
-1 | 参数非法 |
参考用例:
C++
void loadSceneConfig(const std::string &encodeScenefilePath, int scene_id)
{
std::ifstream scene_config_file(encodeScenefilePath);
if (!scene_config_file.is_open())
{
TRACE_ERR("Failed to open scene_config_file: %s", encodeScenefilePath.c_str());
return;
}
try
{
// 读取整个JSON文件
nlohmann::json scene_config;
scene_config_file >> scene_config;
// 查找对应scene_id的配置
nlohmann::json target_scene;
for (auto &scene : scene_config["scene_define"])
{
if (scene["scene_id"] == scene_id)
{
target_scene = scene;
break;
}
}
if (target_scene.empty())
{
TRACE_ERR("Not found scene_id = %d in config", scene_id);
return;
}
// 转换为JSON字符串
std::string scene_config_str = target_scene.dump();
// 调用场景配置接口设置场景配置选项
BJCastSetEncodeSceneConfig(scene_config_str.c_str());
}
catch (const std::exception &e)
{
TRACE_ERR("JSON parse error: %s", e.what());
}
}
int main(int argc, char *argv[])
{
//......
int ret = 0;
ret = BJCastLibInit(¶);
assert(ret == 0);
//只有在正确初始化SDK函数后才能进行后面操作!!!
// sceneConf文件路径
std::string encodeScenefilePath = GetEncodeSceneConfigFilePath();
TRACE_INFO("Main::Current encodeScenefilePath:%s", encodeScenefilePath.c_str());
std::string sender_id = GetNowTime() + "sender456";
BJCastSenderLibInitPara para;
memset(¶, 0, sizeof(para));
para.channel_id = 10;
para.channel_code = "a38a2e9299408ba90fff5bebc230938d";
para.user_name = "test123";
para.sender_id = sender_id.c_str(); // TODO:should use uuid
para.mac_addr = "";
para.custom_info = "";
para.log_directory = logConfigPath.c_str();
para.encode_scene_conf = encodeScenefilePath.c_str(); // 场景配置文件路径
// 场景配置参数==> 0:低 1:中 2:高
int SCENE_ID = 2;
loadSceneConfig(encodeScenefilePath, SCENE_ID);
return 0;
}
json参考文件 encoder_scene_conf.json:
json
{
"scene_define":[
{
"scene_name":"",
"scene_id":0,
"scene_encoder_config":
[
{
"codec": 1,
"width":1918,
"height":1078,
"min_width":0,
"min_height":0,
"bitrate": 1000,
"framerate": 30,
"qmin":-1,
"qmax":-1
},
{
"codec": 1,
"width":1920,
"height":1080,
"min_width":1918,
"min_height":1078,
"bitrate": 2000,
"framerate": 30,
"qmin":-1,
"qmax":-1
},
{
"codec": 1,
"width":2560,
"height":1440,
"min_width":1922,
"min_height":1082,
"bitrate": 2000,
"framerate": 15,
"qmin":-1,
"qmax":-1
},
{
"codec": 1,
"width":3840,
"height":2160,
"min_width":2562,
"min_height":1442,
"bitrate": 3000,
"framerate": 15,
"qmin":-1,
"qmax":-1
}
]
},
{
"scene_name":"",
"scene_id":1,
"scene_encoder_config":
[
{
"codec": 1,
"width":1918,
"height":1078,
"min_width":0,
"min_height":0,
"bitrate": 3000,
"framerate": 45,
"qmin":-1,
"qmax":-1
},
{
"codec": 1,
"width":1920,
"height":1080,
"min_width":1918,
"min_height":1078,
"bitrate": 4000,
"framerate": 45,
"qmin":-1,
"qmax":-1
},
{
"codec": 1,
"width":2560,
"height":1440,
"min_width":1922,
"min_height":1082,
"bitrate": 3000,
"framerate": 30,
"qmin":-1,
"qmax":-1
},
{
"codec": 1,
"width":3840,
"height":2160,
"min_width":2562,
"min_height":1442,
"bitrate": 4000,
"framerate": 30,
"qmin":-1,
"qmax":-1
}
]
},
{
"scene_name":"",
"scene_id":2,
"scene_encoder_config":
[
{
"codec": 1,
"width":1918,
"height":1078,
"min_width":0,
"min_height":0,
"bitrate": 6000,
"framerate": 60,
"qmin":-1,
"qmax":-1
},
{
"codec": 1,
"width":1920,
"height":1080,
"min_width":1918,
"min_height":1078,
"bitrate": 8000,
"framerate": 60,
"qmin":-1,
"qmax":-1
},
{
"codec": 1,
"width":2560,
"height":1440,
"min_width":1922,
"min_height":1082,
"bitrate": 6000,
"framerate": 60,
"qmin":-1,
"qmax":-1
},
{
"codec": 1,
"width":3840,
"height":2160,
"min_width":2562,
"min_height":1442,
"bitrate": 8000,
"framerate": 30,
"qmin":-1,
"qmax":-1
}
]
}
]
}
4 常见错误码说明
- E_OK = 0; // 成功
- E_FAILURE = -1; // 失败,参数非法,错误
- E_NULL_POINTER = -2; // 空指针异常
- E_NOT_INITIALIZED = -3; // 未初始化
- E_ALREADY_INITIALIZED = -4; // 已经初始化
- E_NOT_IMPLEMENTED = -5; // 未实现
- E_NETWORK_FAILURE = -6; // 网络异常
- E_TIMEOUT = -7; // 连接超时
- E_INVALID_PARAMETER = -8; // 无效参数
- E_AUTHENTICATION_REQUIRED = -9; // 请求授权认证
- E_AUTHENTICATION_FAILED = -10; // 授权认证失败
- E_DECODE_MESSAGE_FAILURE = -11; // 解码信息失败
- E_ENCODE_MESSAGE_FAILURE = -12; // 编码信息失败
- E_CALL_NOT_FOUND = -13; // 请求未找到
- E_CALL_REJECTED = -14; // 请求被拒绝
- E_CALL_NO_SCREEN_RESOURCE = -15; // 没有屏幕资源
- E_AUTH_FAILED = -16; // Pin码认证失败
- E_HEARTBAET_LOST = -17; // 心跳丢失
- E_CONNECT_FAILED = -18; // 连接失败
- E_ENCOER_PARAM_INVALID = -19; // 编码参数无效
- E_NO_WINDOW_MEDIA_STREAM = -20; // 没有窗口媒体流
- E_TARGET_WINDOW_NOT_EXIST = -30; // 目标窗口不存在
- E_HEARTBAET_REJECT = -31; // 心跳拒绝
- Result E_MEDIA_OPEN_ERR = -51; // Media部分错误