基於RTMP實現Linux|麒麟操做系統下屏幕|系統聲音採集推送

 背景

Windows操做系統自問世以來,以其簡單易用的圖形化界面操做受到大衆追捧,爲計算機的普及、科技的發展作出了不可磨滅的功績,也慢慢的成爲人們最依賴的操做系統。在中國,90%以上的辦公環境都是Windows,學校和各類培訓班的培訓內容也都是Windows,Windows操做系統已經滲入到各行各業,人們已經習慣了Windows的界面和操做習慣,IT計算機和Windows已經被習覺得常的畫上了等號。windows

可是,咱們使用的軟件真的安全嗎?黑屏事件和棱鏡門事件讓不少樂觀看待或還沒有意識到信息安全問題的人們警醒,咱們所使用的國外軟件並非安全的,咱們的數據徹底掌握在別人手中。隨着信息安全上升到了國家戰略的高度,推行自主可控的國產操做系統勢在必行。國產操做系統做爲自主可控的基礎,市場迅速升溫,受到了社會各界的高度關注。api

咱們有別的選擇嗎?安全

其實基於開源軟件Linux二次開發的操做系統,近年來的發展趨勢很是迅猛。Linux已經有20年曆史,尤爲近十年通過日新月異的發展,Linux桌面操做系統已經遠遠擺脫了「具有與主流桌面操做系統的可比性」階段,基於擁有衆多優秀的開源應用軟件的基礎,在軟件多樣性、硬件兼容性、用戶體驗等各方面作了大量的改進,如今已經能夠知足平常辦公的需求。在歐美,咱們不時聽到一些政府部門將採用Linux桌面辦公:慕尼黑市政府用十年的時間,成功的「趕走」了微軟;伯明翰市政府、法國國會、瑞士、挪威和南非政府部門也都採用了Linux桌面辦公。app

國產操做系統|Linux下RTMP同屏推送

在發佈國產操做系統|Linux平臺的RTMP直播推送SDK以前,大牛直播SDK(官方)的RTMP推送模塊已穩定運行在Windows、Android和iOS平臺幾年了。ide

相對Windows、Android和iOS平臺,Linux在桌面採集等方面,資料很是少,數據採集能夠採用調用XLib相關接口實現,本Demo實現的是Linux上實現桌面和系統聲音採集,而後使用RTMP協議推出去的一個SDK. 集成調用很是簡單。oop

相關實現

int main(int argc, char *argv[])
{
	signal(SIGINT, &OnSigIntHandler);

	//printf("sizeof(NT_SmartPublisherSDKAPI)=%d\n", sizeof(NT_SmartPublisherSDKAPI));

	LogInit();

	NT_SmartPublisherSDKAPI push_api;
	if (!PushSDKInit(push_api))
	{
		return 0;
	}

	auto push_handle = StartPush(&push_api, "rtmp://192.168.0.154:1935/live/test1", 30);
	if (!push_handle)
	{
		fprintf(stderr, "start push failed.\n");
		push_api.UnInit();
		return 0;
	}

	while (!g_is_exit)
	{
		sleep(2);
	}

	fprintf(stdout, "Skip run loop, is_exit:%d\n", g_is_exit);

	push_api.StopPublisher(push_handle);

	push_api.Close(push_handle);

	push_handle = nullptr;
	
	push_api.UnInit();

	fprintf(stdout, "SDK UnInit..\n");
	
	return 0;
}

 

相關初始化測試

void OnSigIntHandler(int sig)
	{
		if (SIGINT == sig)
		{
			g_is_exit = true;
		}
	}
	
	void LogInit()
	{
		SmartLogAPI log_api;
		memset(&log_api, 0, sizeof(log_api));
		GetSmartLogAPI(&log_api);

		log_api.SetLevel(SL_INFO_LEVEL);
		log_api.SetPath((NT_PVOID)"./");
	}

	bool PushSDKInit(NT_SmartPublisherSDKAPI& push_api)
	{
		memset(&push_api, 0, sizeof(push_api));
		NT_GetSmartPublisherSDKAPI(&push_api);

		auto ret = push_api.Init(0, nullptr);
		if (NT_ERC_OK != ret)
		{
			fprintf(stderr, "push_api.Init failed!\n");
			return false;
		}
		else
		{
			fprintf(stdout, "push_api.Init ok!\n");
		}

		return true;
	}

推送接口封裝ui

NT_HANDLE StartPush(NT_SmartPublisherSDKAPI* push_api, const std::string& rtmp_url, int dst_fps)
	{
		NT_INT32 pulse_device_number = 0;
		if (NT_ERC_OK == push_api->GetAuidoInputDeviceNumber(2, &pulse_device_number))
		{
			fprintf(stdout, "Pulse device num:%d\n", pulse_device_number);
			char device_name[512];

			for (auto i = 0; i < pulse_device_number; ++i)
			{
				if (NT_ERC_OK == push_api->GetAuidoInputDeviceName(2, i, device_name, 512))
				{
					fprintf(stdout, "index:%d name:%s\n", i, device_name);
				}
			}
		}

		NT_INT32 alsa_device_number = 0;
		if (pulse_device_number < 1)
		{
			if (NT_ERC_OK == push_api->GetAuidoInputDeviceNumber(1, &alsa_device_number))
			{
				fprintf(stdout, "Alsa device num:%d\n", alsa_device_number);
				char device_name[512];
				for (auto i = 0; i < alsa_device_number; ++i)
				{
					if (NT_ERC_OK == push_api->GetAuidoInputDeviceName(1, i, device_name, 512))
					{
						fprintf(stdout, "index:%d name:%s\n", i, device_name);
					}
				}
			}
		}

		NT_INT32 capture_speaker_flag = 0;
		if ( NT_ERC_OK == push_api->IsCanCaptureSpeaker(2, &capture_speaker_flag) )
		{
			if (capture_speaker_flag)
				fprintf(stdout, "Support speaker capture\n");
			else
				fprintf(stdout, "UnSupport speaker capture\n");
		}

		NT_INT32 is_support_window_capture = 0;
		if (NT_ERC_OK == push_api->IsCaptureWindowSupported(NULL, &is_support_window_capture))
		{
			if (is_support_window_capture)
				fprintf(stdout, "Support window capture\n");
			else
				fprintf(stdout, "UnSupport window capture\n");
		}

		NT_HANDLE push_handle = nullptr;

		// if (NT_ERC_OK != push_api->Open(&push_handle, NT_PB_E_VIDEO_OPTION_LAYER, NT_PB_E_AUDIO_OPTION_CAPTURE_SPEAKER, 0, NULL))
		if (NT_ERC_OK != push_api->Open(&push_handle, NT_PB_E_VIDEO_OPTION_SCREEN, NT_PB_E_AUDIO_OPTION_CAPTURE_SPEAKER, 0, NULL))
		{
			return nullptr;
		}

		//push_api->SetXDisplayName(push_handle, ":0");
		//push_api->SetXDisplayName(push_handle, NULL);
		
		// 視頻層配置方式

		//std::vector<std::shared_ptr<nt_pb_sdk::layer_conf_wrapper_base> > layer_confs;

		//auto index = 0;

		//// 第0層填充RGBA矩形, 目的是保證幀率, 顏色就填充全黑
		//auto rgba_layer_c0 = std::make_shared<nt_pb_sdk::RGBARectangleLayerConfigWrapper>(index++, true, 0, 0, 1280, 720);

		//rgba_layer_c0->conf_.red_ = 0;
		//rgba_layer_c0->conf_.green_ = 0;
		//rgba_layer_c0->conf_.blue_ = 0;
		//rgba_layer_c0->conf_.alpha_ = 255;

		//layer_confs.push_back(rgba_layer_c0);

		//// 第一層爲桌面層
		//auto screen_layer_c1 = std::make_shared<nt_pb_sdk::ScreenLayerConfigWrapper>(index++, true, 0, 0, 1280, 720);
		//
		//screen_layer_c1->conf_.scale_filter_mode_ = 3;

		//layer_confs.push_back(screen_layer_c1);

		//std::vector<const NT_PB_LayerBaseConfig* > layer_base_confs;

		//for (const auto& i : layer_confs)
		//{
		//	layer_base_confs.push_back(i->getBase());
		//}

		//if (NT_ERC_OK != push_api->SetLayersConfig(push_handle, 0, layer_base_confs.data(),
		//	layer_base_confs.size(), 0, nullptr))
		//{
		//	push_api->Close(push_handle);
		//	push_handle = nullptr;
		//	return nullptr;
		//}

		// push_api->SetScreenClip(push_handle, 0, 0, 1280, 720);

		push_api->SetFrameRate(push_handle, dst_fps); // 幀率設置
			
		push_api->SetVideoBitRate(push_handle, 2000);  // 平均碼率2000kbps
		push_api->SetVideoQualityV2(push_handle, 26); 
		push_api->SetVideoMaxBitRate(push_handle, 4000); // 最大碼率4000kbps
		push_api->SetVideoKeyFrameInterval(push_handle, dst_fps*2); // 關鍵幀間隔
		push_api->SetVideoEncoderProfile(push_handle, 3); // h264 baseline
		push_api->SetVideoEncoderSpeed(push_handle, 3); // 編碼速度設置到3

		if (pulse_device_number > 0)
		{
			push_api->SetAudioInputLayer(push_handle, 2);
			push_api->SetAuidoInputDeviceId(push_handle, 0);
		}
		else if (alsa_device_number > 0)
		{
			push_api->SetAudioInputLayer(push_handle, 1);
			push_api->SetAuidoInputDeviceId(push_handle, 0);
		}

		// 音頻配置
		push_api->SetPublisherAudioCodecType(push_handle, 1);
		//push_api->SetMute(push_handle, 1);

		if ( NT_ERC_OK != push_api->SetURL(push_handle, rtmp_url.c_str(), NULL) )
		{
			push_api->Close(push_handle);
			push_handle = nullptr;
			return nullptr;
		}

		if ( NT_ERC_OK != push_api->StartPublisher(push_handle, NULL) )
		{
			push_api->Close(push_handle);
			push_handle = nullptr;
			return nullptr;
		}

		return push_handle;
	}

相關界面編碼

總結

實際測試下來,以RTMP推送和RTMP播放爲例,總體測試時延都在毫秒級,可知足像內網無紙化、教育同屏等場景需求。url

Linux的RTMP推送接口,和Windows平臺的相差無幾,通用接口四個平臺幾乎一致,不存在集成難度。

國產操做系統替代windows,也許並不像你想象的那麼遙遠,使用習慣上也並不像想象中那麼難,相信在不久的未來,國產操做系統會真正被市場普遍使用。

國產操做系統下的應用生態,須要國人一點點創建。

相關文章
相關標籤/搜索