Skip to content

当前版本:V1.1.5

©苏州必捷网络有限公司

1 概述

1.1 目的

用于指导用户使用BJSDK开发国产操作系统发送端应用程序。

1.2 读者对象

本文档适用于国产操作系统发送端应用程序的开发人员和测试人员。

1.3 缩略语定义

缩写名称英文中文
BJCastBJCast必捷无线投屏协议

2 范围

2.1 功能

本SDK为Linux发射端BJSDK,和BJCast接收端SDK配合使用进行无线投屏。

本SDK libbjcast_project.solibbjcast_sender.so 等so库为标准的动态库,配套 bjcast_project_lib.h 头文件,提供API接口供应用集成。

2.2 总体框架

Linux发射端【必捷投屏】总体框架分为三层:

  1. SDK层:libbjcast_project.so 和 **libbjcast_sender.so ** 等so库为动态库。实现了投屏控制协议、录屏功能、媒体传输和处理协议。头文件 bjcast_project_lib.h 提供C接口供外部应用集成。

  2. addon层:我司使用addon集成调用SDK里面的方法,封装后提供addon接口供应用层调用。

    (PS:客户可按需求使用其他方式集成调用SDK里面的方法)

  3. 应用层:我司使用addon提供的接口完成应用层前端界面相应功能的实现。

2.3 SDK交付物

  • SDK库文件
  • SDK接口文档

3 API接口

3.1 初始化SDK接口

C
int32_t BJCastLibInit(const BJCastSenderLibInitPara *initPara);

描述:

初始化SDK,App在启动时作初始化调用。

参数:

BJCastSenderLibInitPara: SDK初始化参数结构体,包含以下参数:

参数名描述必填
BJCastSenderLibInfListenerSDK初始化回调接口结构体,由应用层实现
username用户名
sender_id发射端的UUID,由应用层设定生成,必须保证唯一,接收端根据这个值来判断是不是重连
mac_addrMac地址,如果不需要则初始化为""
custom_info用户投屏时携带的扩展字段,最大长度256字节,如果不需要该功能,设为""
encode_scene_conf编码器场景参数配置,参考典型配置文件,如果不需设置,请设为""
encoder_ext_conf编码器的额外参数设置,参考ffmpeg
channel_idSDK渠道ID,由BJ提供
channel_codeSDK渠道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_idchannel_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(&para, 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(&para);  //调用初始化接口完成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(&para);
	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(&para);
	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投屏失败
-121SDK channel 信息错误

OnSessionEnd 回调函数有以下返回值:

返回值含义
2接收端踢出投屏
0退出投屏
-17心跳丢失

OnStartSessionResult 回调函数有以下返回值:

返回值含义
0投屏成功
-6网络异常
-7请求超时
-8无效参数
-14当前投屏被拒绝
-15接收端投屏已满
-16PIN码错误或触发PIN码投屏事件
-51媒体部分错误

OnSessionNotify 回调函数有以下返回值:

返回值含义
0请求全屏成功 / 退出全屏成功

**参考用例: ** <开始投屏函数>,只有在正确初始化SDK函数后才能进行后面操作!

C/C++代码:

c
int main(int argc, char *argv[])
{
    //......
	int ret = 0;
	ret = BJCastLibInit(&para);
	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_confjson格式的投屏质量场景配置字符串

返回值:

返回值含义
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(&para);
	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(&para, 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部分错误