void FGameLiveStreaming::StopBroadcastingGame()
{
	if( bIsBroadcasting )
	{
		if( LiveStreamer != nullptr )
		{
			if( LiveStreamer->IsBroadcasting() )
			{
				LiveStreamer->StopWebCam();
				LiveStreamer->StopBroadcasting();
			}
		}
		if( FSlateApplication::IsInitialized() )	// During shutdown, Slate may have already been destroyed by the time our viewport gets cleaned up
		{
			FSlateRenderer* SlateRenderer = FSlateApplication::Get().GetRenderer().Get();
			SlateRenderer->OnSlateWindowRendered().RemoveAll( this );
		}

		// Cleanup readback buffer textures
		{
			FlushRenderingCommands();

			ReadbackTextures[0].SafeRelease();
			ReadbackTextures[1].SafeRelease();
			ReadbackTextureIndex = 0;
			ReadbackBuffers[0] = nullptr;
			ReadbackBuffers[1] = nullptr;
			ReadbackBufferIndex = 0;
		}
	}
}
void FEditorLiveStreaming::BroadcastEditorVideoFrame()
{
	if( ensure( IsBroadcastingEditor() ) )
	{
		// Check to see if we're streaming live video.  If so, we'll want to push new frames to be broadcast.
		if( LiveStreamer->IsBroadcasting() )
		{
			// Is this live streamer ready to accept new frames?
			if( LiveStreamer->IsReadyForVideoFrames() )
			{
				FSlateRenderer* SlateRenderer = FSlateApplication::Get().GetRenderer().Get();

				const FMappedTextureBuffer& CurrentBuffer = ReadbackBuffers[ReadbackBufferIndex];

				if ( CurrentBuffer.IsValid() )
				{
					//TODO PushVideoFrame Needs to Take Width and Height
					LiveStreamer->PushVideoFrame((FColor*)CurrentBuffer.Data/*, CurrentBuffer.Width, CurrentBuffer.Height*/);
					++SubmittedVideoFrameCount;

					// If this is the first frame we've submitted, then we can fade out our notification UI, since broadcasting is in progress
					if( SubmittedVideoFrameCount == 1 )
					{
						TSharedPtr< SNotificationItem > Notification( NotificationWeakPtr.Pin() );
						if( Notification.IsValid() )
						{
							Notification->ExpireAndFadeout();
						}
					}

					SlateRenderer->UnmapVirtualScreenBuffer();
				}

				TArray<FString> UnusedKeypressBuffer;
				SlateRenderer->CopyWindowsToVirtualScreenBuffer( UnusedKeypressBuffer );
				SlateRenderer->MapVirtualScreenBuffer(&ReadbackBuffers[ReadbackBufferIndex]);

				// Ping pong between buffers
				ReadbackBufferIndex = ( ReadbackBufferIndex + 1 ) % 2;
			}
		}
		else
		{
			// If our streaming service has stopped broadcasting for some reason, then we'll cancel our broadcast
			StopBroadcastingEditor();
		}
	}
}
void FRenderDocPluginModule::OnEditorLoaded(SWindow& SlateWindow, void* ViewportRHIPtr)
{
	// --> YAGER by SKrysanov 6/11/2014 : fixed crash on removing this callback in render thread.
	if (IsInGameThread())
	{
		FSlateRenderer* SlateRenderer = FSlateApplication::Get().GetRenderer().Get();
		SlateRenderer->OnSlateWindowRendered().Remove(LoadedDelegateHandle);
	}
	// <-- YAGER by SKrysanov 6/11/2014

	if (IsInitialized)
	{
		return;
	}
	IsInitialized = true;

	if (GConfig)
	{
		bool bGreetingHasBeenShown;
		GConfig->GetBool(TEXT("RenderDoc"), TEXT("GreetingHasBeenShown"), bGreetingHasBeenShown, GGameIni);
		if (!bGreetingHasBeenShown)
		{
			GEditor->EditorAddModalWindow(SNew(SRenderDocPluginAboutWindow));
			GConfig->SetBool(TEXT("RenderDoc"), TEXT("GreetingHasBeenShown"), true, GGameIni);
		}
	}

	//TODO: REMOVE THIS WHEN WE GET PULL REQUEST ACCEPTED

	HWND WindowHandle = GetActiveWindow();

	//Trigger a capture just to make sure we are set up correctly. This should prevent us from crashing on exit.
	ENQUEUE_UNIQUE_RENDER_COMMAND_FOURPARAMETER(
		InitializeRenderDoc,
		HWND, WindowHandle, WindowHandle,
		FRenderDocPluginGUI*, RenderDocGUI, RenderDocGUI,
		pRENDERDOC_StartFrameCapture, RenderDocStartFrameCapture, RenderDocStartFrameCapture,
		pRENDERDOC_EndFrameCapture, RenderDocEndFrameCapture, RenderDocEndFrameCapture,
		{
		RenderDocStartFrameCapture(WindowHandle);
		RenderDocEndFrameCapture(WindowHandle);

		FString NewestCapture = RenderDocGUI->GetNewestCapture(FPaths::Combine(*FPaths::GameSavedDir(), *FString("RenderDocCaptures")));
		IFileManager::Get().Delete(*NewestCapture);
		});
Example #4
0
void FWebMRecord::OnWorldCreated(UWorld* World, const UWorld::InitializationValues IVS)
{
	if (IsRunningCommandlet() || IsRunningDedicatedServer())
	{
		return;
	}

	if (!bRegisteredSlateDelegate)
	{
		FSlateRenderer* SlateRenderer = FSlateApplication::Get().GetRenderer().Get();
		SlateRenderer->OnSlateWindowRendered().AddRaw(this, &FWebMRecord::OnSlateWindowRenderedDuringCapture);
		bRegisteredSlateDelegate = true;

		// Setup readback buffer textures
		{
			for (int32 TextureIndex = 0; TextureIndex < 2; ++TextureIndex)
			{
				FRHIResourceCreateInfo CreateInfo;
				ReadbackTextures[TextureIndex] = RHICreateTexture2D(
					VideoWidth,
					VideoHeight,
					PF_B8G8R8A8,
					1,
					1,
					TexCreate_CPUReadback,
					CreateInfo
					);
			}

			ReadbackTextureIndex = 0;

			ReadbackBuffers[0] = nullptr;
			ReadbackBuffers[1] = nullptr;
			ReadbackBufferIndex = 0;
		}
	}
}
void FEditorLiveStreaming::StartBroadcastingEditor()
{
	if( !IsBroadcastingEditor() && IsLiveStreamingAvailable() )
	{
		// Select a live streaming service
		{
			static const FName LiveStreamingFeatureName( "LiveStreaming" );
			LiveStreamer = &IModularFeatures::Get().GetModularFeature<ILiveStreamingService>( LiveStreamingFeatureName );
		}

		// Register to find out about status changes
		LiveStreamer->OnStatusChanged().AddRaw( this, &FEditorLiveStreaming::BroadcastStatusCallback );

		// @todo livestream: Allow connection to chat independently from broadcasting? (see removing delegate too)
		LiveStreamer->OnChatMessage().AddRaw( this, &FEditorLiveStreaming::OnChatMessage );


		// Tell our live streaming plugin to start broadcasting
		{
			const auto& Settings = *GetDefault< UEditorLiveStreamingSettings >();
			FSlateRenderer* SlateRenderer = FSlateApplication::Get().GetRenderer().Get();
			const FIntRect VirtualScreen = SlateRenderer->SetupVirtualScreenBuffer( Settings.bPrimaryMonitorOnly, Settings.ScreenScaling, LiveStreamer );

			bIsBroadcasting = true;
			SubmittedVideoFrameCount = 0;

			// @todo livestream: What about if virtual screen size changes while we are still broadcasting?  For example, if the user changes their
			// desktop resolution while the editor is running.  We'd need to stop and restart the broadcast.
			FBroadcastConfig BroadcastConfig;
			BroadcastConfig.VideoBufferWidth = VirtualScreen.Width();
			BroadcastConfig.VideoBufferHeight = VirtualScreen.Height();
			BroadcastConfig.FramesPerSecond = Settings.FrameRate;
			BroadcastConfig.PixelFormat = FBroadcastConfig::EBroadcastPixelFormat::R8G8B8A8;
			BroadcastConfig.bCaptureAudioFromComputer = Settings.bCaptureAudioFromComputer;
			BroadcastConfig.bCaptureAudioFromMicrophone = Settings.bCaptureAudioFromMicrophone;
			LiveStreamer->StartBroadcasting( BroadcastConfig );

			if( Settings.bEnableWebCam )
			{
				FWebCamConfig WebCamConfig;
				switch( Settings.WebCamResolution )
				{
					case EEditorLiveStreamingWebCamResolution::Normal_320x240:
						WebCamConfig.DesiredWebCamWidth = 320;
						WebCamConfig.DesiredWebCamHeight = 240;
						break;
					case EEditorLiveStreamingWebCamResolution::Wide_320x180:
						WebCamConfig.DesiredWebCamWidth = 320;
						WebCamConfig.DesiredWebCamHeight = 180;
						break;
					case EEditorLiveStreamingWebCamResolution::Normal_640x480:
						WebCamConfig.DesiredWebCamWidth = 640;
						WebCamConfig.DesiredWebCamHeight = 480;
						break;
					case EEditorLiveStreamingWebCamResolution::Wide_640x360:
						WebCamConfig.DesiredWebCamWidth = 640;
						WebCamConfig.DesiredWebCamHeight = 360;
						break;
					case EEditorLiveStreamingWebCamResolution::Normal_800x600:
						WebCamConfig.DesiredWebCamWidth = 800;
						WebCamConfig.DesiredWebCamHeight = 600;
						break;
					case EEditorLiveStreamingWebCamResolution::Wide_800x450:
						WebCamConfig.DesiredWebCamWidth = 800;
						WebCamConfig.DesiredWebCamHeight = 450;
						break;
					case EEditorLiveStreamingWebCamResolution::Normal_1024x768:
						WebCamConfig.DesiredWebCamWidth = 1024;
						WebCamConfig.DesiredWebCamHeight = 768;
						break;
					case EEditorLiveStreamingWebCamResolution::Wide_1024x576:
						WebCamConfig.DesiredWebCamWidth = 1024;
						WebCamConfig.DesiredWebCamHeight = 576;
						break;
					case EEditorLiveStreamingWebCamResolution::Normal_1080x810:
						WebCamConfig.DesiredWebCamWidth = 1080;
						WebCamConfig.DesiredWebCamHeight = 810;
						break;
					case EEditorLiveStreamingWebCamResolution::Wide_1080x720:
						WebCamConfig.DesiredWebCamWidth = 1080;
						WebCamConfig.DesiredWebCamHeight = 720;
						break;
					case EEditorLiveStreamingWebCamResolution::Normal_1280x960:
						WebCamConfig.DesiredWebCamWidth = 1280;
						WebCamConfig.DesiredWebCamHeight = 960;
						break;
					case EEditorLiveStreamingWebCamResolution::Wide_1280x720:
						WebCamConfig.DesiredWebCamWidth = 1280;
						WebCamConfig.DesiredWebCamHeight = 720;
						break;
					case EEditorLiveStreamingWebCamResolution::Normal_1920x1440:
						WebCamConfig.DesiredWebCamWidth = 1920;
						WebCamConfig.DesiredWebCamHeight = 1440;
						break;
					case EEditorLiveStreamingWebCamResolution::Wide_1920x1080:
						WebCamConfig.DesiredWebCamWidth = 1920;
						WebCamConfig.DesiredWebCamHeight = 1080;
						break;

					default:
						check(0);
						break;
				}

				// @todo livestream: Allow web cam to be started/stopped independently from the broadcast itself, so users can setup their web cam
				LiveStreamer->StartWebCam( WebCamConfig );
			}
		}
	}
}
void FGameLiveStreaming::StartBroadcastingGame( const FGameBroadcastConfig& GameBroadcastConfig )
{
	if( !IsBroadcastingGame() )
	{
		if( bIsBroadcasting )
		{
			FSlateRenderer* SlateRenderer = FSlateApplication::Get().GetRenderer().Get();
			SlateRenderer->OnSlateWindowRendered().RemoveAll( this );
			bIsBroadcasting = false;
		}

		// We can GetLiveStreamingService() here to fill in our LiveStreamer variable lazily, to make sure the service plugin is loaded 
		// before we try to cache it's interface pointer
		if( GetLiveStreamingService() != nullptr  )
		{
			FSlateRenderer* SlateRenderer = FSlateApplication::Get().GetRenderer().Get();
			SlateRenderer->OnSlateWindowRendered().AddRaw( this, &FGameLiveStreaming::OnSlateWindowRenderedDuringBroadcasting );

			this->bMirrorWebCamImage = GameBroadcastConfig.bMirrorWebCamImage;
			this->bDrawSimpleWebCamVideo = GameBroadcastConfig.bDrawSimpleWebCamVideo;

			// @todo livestream: This will interfere with editor live streaming if both are running at the same time!  The editor live 
			// streaming does check to make sure that game isn't already broadcasting, but the game currently doesn't have a good way to
			// do that, besides asking the LiveStreamer itself.

			UGameViewportClient* GameViewportClient = GEngine->GameViewport;
			check( GameViewportClient != nullptr );

			// @todo livestream: What about if viewport size changes while we are still broadcasting?  We need to restart the broadcast!
			FBroadcastConfig BroadcastConfig;
			BroadcastConfig.VideoBufferWidth = GameViewportClient->Viewport->GetSizeXY().X;
			BroadcastConfig.VideoBufferHeight = GameViewportClient->Viewport->GetSizeXY().Y;

			BroadcastConfig.VideoBufferWidth = FPlatformMath::FloorToInt( (float)BroadcastConfig.VideoBufferWidth * GameBroadcastConfig.ScreenScaling );
			BroadcastConfig.VideoBufferHeight = FPlatformMath::FloorToInt( (float)BroadcastConfig.VideoBufferHeight * GameBroadcastConfig.ScreenScaling );

			// Fix up the desired resolution so that it will work with the streaming system.  Some broadcasters require the
			// video buffer to be multiples of specific values, such as 32
			// @todo livestream: This could cause the aspect ratio to be changed and the buffer to be stretched non-uniformly, but usually the aspect only changes slightly
			LiveStreamer->MakeValidVideoBufferResolution( BroadcastConfig.VideoBufferWidth, BroadcastConfig.VideoBufferHeight );

			// Setup readback buffer textures
			{
				for( int32 TextureIndex = 0; TextureIndex < 2; ++TextureIndex )
				{
					FRHIResourceCreateInfo CreateInfo;
					ReadbackTextures[ TextureIndex ] = RHICreateTexture2D(
						BroadcastConfig.VideoBufferWidth,
						BroadcastConfig.VideoBufferHeight,
						PF_B8G8R8A8,
						1,
						1,
						TexCreate_CPUReadback,
						CreateInfo
						);
				}
	
				ReadbackTextureIndex = 0;

				ReadbackBuffers[0] = nullptr;
				ReadbackBuffers[1] = nullptr;
				ReadbackBufferIndex = 0;
			}

			BroadcastConfig.FramesPerSecond = GameBroadcastConfig.FrameRate;
			BroadcastConfig.PixelFormat = FBroadcastConfig::EBroadcastPixelFormat::B8G8R8A8;	// Matches viewport backbuffer format
			BroadcastConfig.bCaptureAudioFromComputer = GameBroadcastConfig.bCaptureAudioFromComputer;
			BroadcastConfig.bCaptureAudioFromMicrophone = GameBroadcastConfig.bCaptureAudioFromMicrophone;
			LiveStreamer->StartBroadcasting( BroadcastConfig );

			if( GameBroadcastConfig.bEnableWebCam )
			{
				// @todo livestream: Allow web cam to be started/stopped independently from the broadcast itself, so users can setup their web cam
				FWebCamConfig WebCamConfig;
				WebCamConfig.DesiredWebCamWidth = GameBroadcastConfig.DesiredWebCamWidth;
				WebCamConfig.DesiredWebCamHeight = GameBroadcastConfig.DesiredWebCamHeight;
				LiveStreamer->StartWebCam( WebCamConfig );
			}

			bIsBroadcasting = true;
		}
	}
}
void FRenderDocPluginModule::StartupModule()
{
	//Load DLL
	FString BinaryPath;
	if (GConfig)
	{
		GConfig->GetString(TEXT("RenderDoc"), TEXT("BinaryPath"), BinaryPath, GGameIni);
	}
	FString PathToRenderDocDLL = FPaths::Combine(*BinaryPath, *FString("renderdoc.dll"));

	RenderDocDLL = NULL;
	RenderDocDLL = GetModuleHandle(*PathToRenderDocDLL);
	if (BinaryPath.IsEmpty() || !RenderDocDLL)
	{
		UE_LOG(RenderDocPlugin, Error, TEXT("Could not find the renderdoc DLL, have you loaded the RenderDocLoaderPlugin?"));
		return;
	}
	
	//Init function pointers
	RenderDocGetAPIVersion = (pRENDERDOC_GetAPIVersion)GetRenderDocFunctionPointer(RenderDocDLL, "RENDERDOC_GetAPIVersion");
	RenderDocSetLogFile = (pRENDERDOC_SetLogFile)GetRenderDocFunctionPointer(RenderDocDLL, "RENDERDOC_SetLogFile");
	
	RenderDocSetCaptureOptions = (pRENDERDOC_SetCaptureOptions)GetRenderDocFunctionPointer(RenderDocDLL, "RENDERDOC_SetCaptureOptions");
	RenderDocGetCapture = (pRENDERDOC_GetCapture)GetRenderDocFunctionPointer(RenderDocDLL, "RENDERDOC_GetCapture");
	RenderDocSetActiveWindow = (pRENDERDOC_SetActiveWindow)GetRenderDocFunctionPointer(RenderDocDLL, "RENDERDOC_SetActiveWindow");
	RenderDocTriggerCapture = (pRENDERDOC_TriggerCapture)GetRenderDocFunctionPointer(RenderDocDLL, "RENDERDOC_TriggerCapture");
	RenderDocStartFrameCapture = (pRENDERDOC_StartFrameCapture)GetRenderDocFunctionPointer(RenderDocDLL, "RENDERDOC_StartFrameCapture");
	RenderDocEndFrameCapture = (pRENDERDOC_EndFrameCapture)GetRenderDocFunctionPointer(RenderDocDLL, "RENDERDOC_EndFrameCapture");
	
	RenderDocGetOverlayBits = (pRENDERDOC_GetOverlayBits)GetRenderDocFunctionPointer(RenderDocDLL, "RENDERDOC_GetOverlayBits");
	RenderDocMaskOverlayBits = (pRENDERDOC_MaskOverlayBits)GetRenderDocFunctionPointer(RenderDocDLL, "RENDERDOC_MaskOverlayBits");

	RenderDocSetFocusToggleKeys = (pRENDERDOC_SetFocusToggleKeys)GetRenderDocFunctionPointer(RenderDocDLL, "RENDERDOC_SetFocusToggleKeys");
	RenderDocSetCaptureKeys = (pRENDERDOC_SetCaptureKeys)GetRenderDocFunctionPointer(RenderDocDLL, "RENDERDOC_SetCaptureKeys");

	RenderDocInitRemoteAccess = (pRENDERDOC_InitRemoteAccess)GetRenderDocFunctionPointer(RenderDocDLL, "RENDERDOC_InitRemoteAccess");

	//Set capture settings
	FString RenderDocCapturePath = FPaths::Combine(*FPaths::GameSavedDir(), *FString("RenderDocCaptures"));
	if (!IFileManager::Get().DirectoryExists(*RenderDocCapturePath))
	{
		IFileManager::Get().MakeDirectory(*RenderDocCapturePath, true);
	}

	FString CapturePath = FPaths::Combine(*RenderDocCapturePath, *FDateTime::Now().ToString());
	CapturePath = FPaths::ConvertRelativePathToFull(CapturePath);
	FPaths::NormalizeDirectoryName(CapturePath);
	
	if (sizeof(TCHAR) == sizeof(char))
	{
		RenderDocSetLogFile((char*)*CapturePath);
	}
	else
	{
		char CapturePathShort[1024];
		ZeroMemory(CapturePathShort, 1024);
		size_t NumCharsConverted;
		wcstombs_s(&NumCharsConverted, CapturePathShort, *CapturePath, CapturePath.Len());
		RenderDocSetLogFile(CapturePathShort);
	}

	RenderDocSetFocusToggleKeys(NULL, 0);
	RenderDocSetCaptureKeys(NULL, 0);

	CaptureOptions Options = RenderDocSettings.CreateOptions();
	RenderDocSetCaptureOptions(&Options);

	//Init remote access
	SocketPort = 0;
	RenderDocInitRemoteAccess(&SocketPort);

	//Init UI
	FRenderDocPluginStyle::Initialize();
	FRenderDocPluginCommands::Register();

	FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");
	TSharedRef<FUICommandList> CommandBindings = LevelEditorModule.GetGlobalLevelEditorActions();
	ExtensionManager = LevelEditorModule.GetToolBarExtensibilityManager();

	CommandBindings->MapAction(FRenderDocPluginCommands::Get().CaptureFrame,
		FExecuteAction::CreateRaw(this, &FRenderDocPluginModule::CaptureCurrentViewport),
		FCanExecuteAction());
	CommandBindings->MapAction(FRenderDocPluginCommands::Get().OpenSettings,
		FExecuteAction::CreateRaw(this, &FRenderDocPluginModule::OpenSettingsEditorWindow),
		FCanExecuteAction());

	ToolbarExtender = MakeShareable(new FExtender);
	ToolbarExtension = ToolbarExtender->AddToolBarExtension("CameraSpeed", EExtensionHook::After, CommandBindings,
		FToolBarExtensionDelegate::CreateRaw(this, &FRenderDocPluginModule::AddToolbarExtension));

	LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolbarExtender);

	//Init renderdoc
	RenderDocMaskOverlayBits(eOverlay_None, eOverlay_None);

	RenderDocGUI = new FRenderDocPluginGUI(RenderDocGetCapture);

	IsInitialized = false;
	FSlateRenderer* SlateRenderer = FSlateApplication::Get().GetRenderer().Get();
	LoadedDelegateHandle = SlateRenderer->OnSlateWindowRendered().AddRaw(this, &FRenderDocPluginModule::OnEditorLoaded);

	int32 RenderDocVersion = RenderDocGetAPIVersion();
	UE_LOG(RenderDocPlugin, Log, TEXT("RenderDoc plugin started! Your renderdoc installation is v%i"), RenderDocVersion);
}