using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Video;
using UnityEngine.EventSystems;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using FMODUnity;
using FMOD.Studio;
public class DisneyPlusPlayer : MonoBehaviour
{
[Header(“Video Components”)]
public VideoPlayer videoPlayer;
public RenderTexture renderTexture;
public RawImage videoDisplay;
[Header("FMOD Audio")]
public EventReference videoAudioEvent; // FMOD event for video audio
private EventInstance videoAudioInstance;
[Header("UI Components")]
public Text titleText;
public Button nextEpisodeButton;
public Button skipIntroButton;
public Slider episodeProgressBar;
public Text subtitleText;
[Header("UI Containers (for overlay)")]
public CanvasGroup uiOverlayGroup; // Container for all overlay UI elements
public Canvas mainCanvas; // Main canvas for UI
[Header("Content")]
public VideoClip disneyIntro;
public VideoClip[] episodes;
public EventReference[] episodeFMODEvents; // FMOD events for each episode
public string[] episodeTitles;
[Header("Subtitles")]
public bool subtitlesEnabled = false;
public TextAsset[] subtitleFiles;
[Header("Display Settings")]
public bool maintainAspectRatio = true;
public bool fullScreenVideo = true;
public float uiHideDelay = 3f; // Time before UI hides after no mouse movement
[Header("Playback Controls")]
public KeyCode playPauseKey = KeyCode.Space;
public string playPauseButtonPS5 = "X"; // PS5 X button
public string playPauseButtonXbox = "A"; // Xbox A button
public float controllerSeekSpeed = 0.5f; // Speed for controller seeking
[Header("FMOD Parameters")]
public string volumeParameter = "Volume";
public float defaultVolume = 1f;
private int currentEpisode = 0;
private bool introSkipped = false;
private bool isNextEpisodeAutoPlay = false;
private bool isPlayingIntro = false;
private bool isSeeking = false;
private bool isFMODPlaying = false;
private Dictionary<double, string> subtitles;
private Vector3 lastMousePosition;
private float timeSinceLastMouseMove;
private bool isUIVisible = true;
private Coroutine hideUICoroutine;
private Coroutine nextEpisodeCoroutine;
private Coroutine syncCoroutine;
void Start()
{
SetupVideoPlayer();
SetupUI();
SetupFullScreenVideo();
lastMousePosition = Input.mousePosition;
// Wait a frame before playing to ensure everything is set up
StartCoroutine(DelayedStart());
}
IEnumerator DelayedStart()
{
yield return new WaitForEndOfFrame();
PlayIntro();
}
void SetupVideoPlayer()
{
if (videoPlayer == null)
{
videoPlayer = GetComponent<VideoPlayer>();
}
// Create full-screen render texture
CreateFullScreenRenderTexture();
videoPlayer.renderMode = VideoRenderMode.RenderTexture;
videoPlayer.targetTexture = renderTexture;
videoPlayer.waitForFirstFrame = true;
videoPlayer.prepareCompleted += OnVideoPrepared;
videoPlayer.audioOutputMode = VideoAudioOutputMode.None; // Disable Unity audio output
if (videoDisplay != null)
{
videoDisplay.texture = renderTexture;
}
else
{
Debug.LogError("VideoDisplay RawImage is not assigned!");
}
}
void OnVideoPrepared(VideoPlayer vp)
{
Debug.Log("Video prepared and ready to play");
}
void CreateFullScreenRenderTexture()
{
// Get screen dimensions
int screenWidth = Screen.width;
int screenHeight = Screen.height;
// Ensure minimum size
if (screenWidth < 1920) screenWidth = 1920;
if (screenHeight < 1080) screenHeight = 1080;
if (renderTexture != null)
{
renderTexture.Release();
}
renderTexture = new RenderTexture(screenWidth, screenHeight, 24);
renderTexture.antiAliasing = 1;
renderTexture.Create();
Debug.Log($"Created RenderTexture: {screenWidth}x{screenHeight}");
}
void SetupFullScreenVideo()
{
if (videoDisplay != null && fullScreenVideo)
{
// Remove any aspect ratio fitter that might constrain the video
AspectRatioFitter arf = videoDisplay.GetComponent<AspectRatioFitter>();
if (arf != null)
{
DestroyImmediate(arf);
}
// Make video fill entire screen
RectTransform videoRect = videoDisplay.GetComponent<RectTransform>();
if (videoRect != null)
{
videoRect.anchorMin = Vector2.zero;
videoRect.anchorMax = Vector2.one;
videoRect.offsetMin = Vector2.zero;
videoRect.offsetMax = Vector2.zero;
videoRect.anchoredPosition = Vector2.zero;
// Ensure it's behind UI
videoDisplay.transform.SetAsFirstSibling();
}
// Set UV coordinates to fill and crop appropriately
videoDisplay.uvRect = new Rect(0, 0, 1, 1);
}
// Setup canvas for UI overlay
if (mainCanvas == null)
{
mainCanvas = FindFirstObjectByType<Canvas>();
}
if (mainCanvas != null)
{
mainCanvas.renderMode = RenderMode.ScreenSpaceOverlay;
mainCanvas.sortingOrder = 10; // Ensure UI is on top
}
}
void SetupUI()
{
// Create or find UI overlay group
if (uiOverlayGroup == null)
{
// Try to find existing overlay group first
uiOverlayGroup = FindFirstObjectByType<CanvasGroup>();
if (uiOverlayGroup == null)
{
GameObject overlayObj = new GameObject("UI Overlay");
overlayObj.transform.SetParent(mainCanvas.transform, false);
uiOverlayGroup = overlayObj.AddComponent<CanvasGroup>();
RectTransform overlayRect = overlayObj.GetComponent<RectTransform>();
overlayRect.anchorMin = Vector2.zero;
overlayRect.anchorMax = Vector2.one;
overlayRect.offsetMin = Vector2.zero;
overlayRect.offsetMax = Vector2.zero;
}
}
// Position UI elements like Disney+
PositionUIElements();
// Initial UI state - hide everything initially
if (nextEpisodeButton != null) nextEpisodeButton.gameObject.SetActive(false);
if (skipIntroButton != null) skipIntroButton.gameObject.SetActive(false);
if (episodeProgressBar != null) episodeProgressBar.gameObject.SetActive(false);
if (subtitleText != null) subtitleText.gameObject.SetActive(subtitlesEnabled);
if (titleText != null) titleText.gameObject.SetActive(false);
// Setup button listeners
if (skipIntroButton != null) skipIntroButton.onClick.AddListener(SkipIntro);
if (nextEpisodeButton != null) nextEpisodeButton.onClick.AddListener(OnNextEpisodeButtonClicked);
// Setup slider events
SetupSliderEvents();
// Start with UI hidden
if (uiOverlayGroup != null)
{
uiOverlayGroup.alpha = 0f;
isUIVisible = false;
}
}
void SetupSliderEvents()
{
if (episodeProgressBar != null)
{
// Add event triggers for slider
EventTrigger trigger = episodeProgressBar.gameObject.AddComponent<EventTrigger>();
// Pointer down event
var pointerDown = new EventTrigger.Entry();
pointerDown.eventID = EventTriggerType.PointerDown;
pointerDown.callback.AddListener((data) => { OnSliderPointerDown(); });
trigger.triggers.Add(pointerDown);
// Pointer up event
var pointerUp = new EventTrigger.Entry();
pointerUp.eventID = EventTriggerType.PointerUp;
pointerUp.callback.AddListener((data) => { OnSliderPointerUp(); });
trigger.triggers.Add(pointerUp);
}
}
void OnSliderPointerDown()
{
isSeeking = true;
videoPlayer.Pause();
if (isFMODPlaying && videoAudioInstance.isValid())
{
videoAudioInstance.setPaused(true);
}
}
void OnSliderPointerUp()
{
if (isSeeking)
{
isSeeking = false;
SeekToTime(episodeProgressBar.value * videoPlayer.clip.length);
}
}
void SeekToTime(double time)
{
if (videoPlayer.clip == null) return;
time = Mathf.Clamp((float)time, 0, (float)videoPlayer.clip.length);
videoPlayer.time = time;
if (videoAudioInstance.isValid())
{
// Convert seconds to milliseconds for FMOD
int msPosition = (int)(time * 1000);
videoAudioInstance.setTimelinePosition(msPosition);
}
videoPlayer.Play();
if (videoAudioInstance.isValid())
{
videoAudioInstance.setPaused(false);
isFMODPlaying = true;
}
// Update subtitles immediately after seeking
if (subtitlesEnabled)
{
DisplaySubtitle();
}
// Restart sync coroutine
if (syncCoroutine != null)
{
StopCoroutine(syncCoroutine);
}
syncCoroutine = StartCoroutine(SyncFMODWithVideo());
}
void PositionUIElements()
{
// Position title at top-left
if (titleText != null)
{
RectTransform titleRect = titleText.GetComponent<RectTransform>();
titleRect.anchorMin = new Vector2(0, 1);
titleRect.anchorMax = new Vector2(0, 1);
titleRect.anchoredPosition = new Vector2(50, -50);
titleRect.sizeDelta = new Vector2(400, 50);
}
// Position progress bar at bottom
if (episodeProgressBar != null)
{
RectTransform progressRect = episodeProgressBar.GetComponent<RectTransform>();
progressRect.anchorMin = new Vector2(0, 0);
progressRect.anchorMax = new Vector2(1, 0);
progressRect.anchoredPosition = new Vector2(0, 40);
progressRect.sizeDelta = new Vector2(-100, 20);
}
// Position next episode button at bottom-right
if (nextEpisodeButton != null)
{
RectTransform buttonRect = nextEpisodeButton.GetComponent<RectTransform>();
buttonRect.anchorMin = new Vector2(1, 0);
buttonRect.anchorMax = new Vector2(1, 0);
buttonRect.anchoredPosition = new Vector2(-150, 100);
buttonRect.sizeDelta = new Vector2(200, 50);
}
// Position skip intro button at bottom-right
if (skipIntroButton != null)
{
RectTransform skipRect = skipIntroButton.GetComponent<RectTransform>();
skipRect.anchorMin = new Vector2(1, 0);
skipRect.anchorMax = new Vector2(1, 0);
skipRect.anchoredPosition = new Vector2(-100, 100);
skipRect.sizeDelta = new Vector2(150, 40);
}
// Position subtitles at bottom-center
if (subtitleText != null)
{
RectTransform subtitleRect = subtitleText.GetComponent<RectTransform>();
subtitleRect.anchorMin = new Vector2(0.5f, 0);
subtitleRect.anchorMax = new Vector2(0.5f, 0);
subtitleRect.anchoredPosition = new Vector2(0, 80);
subtitleRect.sizeDelta = new Vector2(800, 60);
}
}
void Update()
{
HandleMouseMovement();
HandleKeyboardAndControllerInput();
HandlePlayPauseInput();
HandleControllerSeeking();
if (videoPlayer.isPlaying && !isSeeking)
{
UpdateProgressBar();
if (subtitlesEnabled && subtitles != null && subtitles.Count > 0)
{
DisplaySubtitle();
}
}
}
void HandlePlayPauseInput()
{
bool playPausePressed = false;
// Check keyboard
if (Input.GetKeyDown(playPauseKey))
{
playPausePressed = true;
}
// Check PS5 controller
if (Input.GetButtonDown(playPauseButtonPS5))
{
playPausePressed = true;
}
// Check Xbox controller
if (Input.GetButtonDown(playPauseButtonXbox))
{
playPausePressed = true;
}
if (playPausePressed)
{
TogglePlayPause();
}
}
void TogglePlayPause()
{
if (videoPlayer.isPlaying)
{
videoPlayer.Pause();
if (videoAudioInstance.isValid())
{
videoAudioInstance.setPaused(true);
isFMODPlaying = false;
}
}
else
{
videoPlayer.Play();
if (videoAudioInstance.isValid())
{
videoAudioInstance.setPaused(false);
isFMODPlaying = true;
StartCoroutine(SyncFMODWithVideo());
}
}
// Show UI when pausing/playing
ShowUI();
if (hideUICoroutine != null)
{
StopCoroutine(hideUICoroutine);
}
hideUICoroutine = StartCoroutine(HideUIAfterDelay());
}
void HandleControllerSeeking()
{
if (isSeeking) return; // Don't allow controller seeking while dragging slider
float horizontalInput = Input.GetAxis("Horizontal");
if (Mathf.Abs(horizontalInput) > 0.2f)
{
if (videoPlayer.isPlaying)
{
double seekAmount = horizontalInput * controllerSeekSpeed * Time.deltaTime;
double newTime = videoPlayer.time + seekAmount;
SeekToTime(newTime);
// Update progress bar immediately
if (episodeProgressBar != null && videoPlayer.clip != null)
{
episodeProgressBar.value = (float)(newTime / videoPlayer.clip.length);
}
// Show UI when seeking
ShowUI();
if (hideUICoroutine != null)
{
StopCoroutine(hideUICoroutine);
}
hideUICoroutine = StartCoroutine(HideUIAfterDelay());
}
}
}
void HandleKeyboardAndControllerInput()
{
// Check for any keyboard input
if (Input.anyKeyDown)
{
if (!isUIVisible)
{
ShowUI();
}
if (hideUICoroutine != null)
{
StopCoroutine(hideUICoroutine);
}
hideUICoroutine = StartCoroutine(HideUIAfterDelay());
}
// Check for controller input
if (Input.GetAxis("Horizontal") != 0 || Input.GetAxis("Vertical") != 0 ||
Input.GetButtonDown("Submit") || Input.GetButtonDown("Cancel") ||
Input.GetButtonDown("Jump") || Input.GetButtonDown("Fire1"))
{
if (!isUIVisible)
{
ShowUI();
}
if (hideUICoroutine != null)
{
StopCoroutine(hideUICoroutine);
}
hideUICoroutine = StartCoroutine(HideUIAfterDelay());
}
}
void HandleMouseMovement()
{
Vector3 currentMousePos = Input.mousePosition;
// Check if mouse moved significantly
if (Vector3.Distance(currentMousePos, lastMousePosition) > 1f)
{
lastMousePosition = currentMousePos;
timeSinceLastMouseMove = 0f;
// Show UI if it's hidden
if (!isUIVisible)
{
ShowUI();
}
// Reset hide timer
if (hideUICoroutine != null)
{
StopCoroutine(hideUICoroutine);
}
hideUICoroutine = StartCoroutine(HideUIAfterDelay());
}
else
{
timeSinceLastMouseMove += Time.deltaTime;
}
// Also check for any mouse clicks
if (Input.GetMouseButtonDown(0))
{
if (!isUIVisible)
{
ShowUI();
}
if (hideUICoroutine != null)
{
StopCoroutine(hideUICoroutine);
}
hideUICoroutine = StartCoroutine(HideUIAfterDelay());
}
}
IEnumerator HideUIAfterDelay()
{
yield return new WaitForSeconds(uiHideDelay);
// Don't hide UI during intro or when next episode button is active
if (!isPlayingIntro && (nextEpisodeButton == null || !nextEpisodeButton.gameObject.activeInHierarchy))
{
HideUI();
}
}
void ShowUI()
{
if (uiOverlayGroup != null)
{
isUIVisible = true;
StopAllCoroutines();
StartCoroutine(FadeUI(uiOverlayGroup.alpha, 1f, 0.3f));
// Show appropriate UI elements
if (titleText != null && !string.IsNullOrEmpty(titleText.text))
{
titleText.gameObject.SetActive(true);
}
// Only show progress bar and buttons when UI is visible due to interaction
if (episodeProgressBar != null && videoPlayer.isPlaying && !isPlayingIntro)
{
episodeProgressBar.gameObject.SetActive(true);
}
if (skipIntroButton != null && isPlayingIntro)
{
skipIntroButton.gameObject.SetActive(true);
}
// Always show next episode button if it's active (regardless of why UI is showing)
if (nextEpisodeButton != null && nextEpisodeButton.gameObject.activeSelf)
{
nextEpisodeButton.gameObject.SetActive(true);
}
}
}
void HideUI()
{
if (uiOverlayGroup != null && !isPlayingIntro)
{
isUIVisible = false;
StartCoroutine(FadeUI(uiOverlayGroup.alpha, 0f, 0.5f));
// Hide interactive elements when UI fades out
if (episodeProgressBar != null)
{
episodeProgressBar.gameObject.SetActive(false);
}
if (skipIntroButton != null)
{
skipIntroButton.gameObject.SetActive(false);
}
// Only hide next episode button if it's not in the "filling" state
if (nextEpisodeButton != null && nextEpisodeCoroutine == null)
{
nextEpisodeButton.gameObject.SetActive(false);
}
}
}
IEnumerator FadeUI(float fromAlpha, float toAlpha, float duration)
{
float elapsed = 0f;
while (elapsed < duration)
{
elapsed += Time.deltaTime;
if (uiOverlayGroup != null)
{
uiOverlayGroup.alpha = Mathf.Lerp(fromAlpha, toAlpha, elapsed / duration);
}
yield return null;
}
if (uiOverlayGroup != null)
{
uiOverlayGroup.alpha = toAlpha;
}
}
void PlayIntro()
{
if (disneyIntro == null)
{
Debug.LogWarning("No intro video assigned, skipping to episode");
PlayEpisode(currentEpisode);
return;
}
isPlayingIntro = true;
ShowUI(); // Always show UI during intro
if (skipIntroButton != null)
{
skipIntroButton.gameObject.SetActive(true);
}
if (titleText != null)
{
titleText.text = "Disney+";
titleText.gameObject.SetActive(true);
}
videoPlayer.clip = disneyIntro;
videoPlayer.audioOutputMode = VideoAudioOutputMode.None;
// Play FMOD audio for intro if assigned
if (!videoAudioEvent.IsNull)
{
StopFMODAudio();
videoAudioInstance = RuntimeManager.CreateInstance(videoAudioEvent);
if (videoAudioInstance.isValid())
{
videoAudioInstance.setVolume(defaultVolume);
videoAudioInstance.start();
isFMODPlaying = true;
}
}
videoPlayer.Prepare();
videoPlayer.prepareCompleted += OnIntroPrepared;
}
void OnIntroPrepared(VideoPlayer vp)
{
videoPlayer.prepareCompleted -= OnIntroPrepared;
videoPlayer.Play();
videoPlayer.loopPointReached += OnIntroFinished;
Debug.Log("Playing intro video");
}
void OnIntroFinished(VideoPlayer vp)
{
videoPlayer.loopPointReached -= OnIntroFinished;
isPlayingIntro = false;
if (skipIntroButton != null)
{
skipIntroButton.gameObject.SetActive(false);
}
StopFMODAudio();
PlayEpisode(currentEpisode);
}
void SkipIntro()
{
if (isPlayingIntro)
{
introSkipped = true;
isPlayingIntro = false;
if (skipIntroButton != null)
{
skipIntroButton.gameObject.SetActive(false);
}
videoPlayer.Stop();
StopFMODAudio();
// Remove the intro finished listener to prevent it from firing
videoPlayer.loopPointReached -= OnIntroFinished;
PlayEpisode(currentEpisode);
}
}
void PlayEpisode(int episodeIndex)
{
if (episodes == null || episodeIndex >= episodes.Length)
{
Debug.LogError("Episode index out of range or no episodes assigned!");
return;
}
if (episodes[episodeIndex] == null)
{
Debug.LogError($"Episode {episodeIndex} is null!");
return;
}
if (titleText != null && episodeTitles != null && episodeIndex < episodeTitles.Length)
{
titleText.text = episodeTitles[episodeIndex];
titleText.gameObject.SetActive(true);
}
if (episodeProgressBar != null)
{
episodeProgressBar.gameObject.SetActive(true);
episodeProgressBar.value = 0f;
}
videoPlayer.clip = episodes[episodeIndex];
videoPlayer.audioOutputMode = VideoAudioOutputMode.None;
// Set up FMOD audio for episode
if (episodeFMODEvents != null && episodeIndex < episodeFMODEvents.Length)
{
StopFMODAudio();
videoAudioInstance = RuntimeManager.CreateInstance(episodeFMODEvents[episodeIndex]);
if (videoAudioInstance.isValid())
{
videoAudioInstance.setVolume(defaultVolume);
}
}
videoPlayer.Prepare();
videoPlayer.prepareCompleted += OnEpisodePrepared;
if (subtitlesEnabled && subtitleFiles != null && subtitleFiles.Length > episodeIndex)
{
subtitles = ParseSubtitles(subtitleFiles[episodeIndex]);
}
}
void OnEpisodePrepared(VideoPlayer vp)
{
videoPlayer.prepareCompleted -= OnEpisodePrepared;
videoPlayer.Play();
if (videoAudioInstance.isValid())
{
videoAudioInstance.start();
isFMODPlaying = true;
syncCoroutine = StartCoroutine(SyncFMODWithVideo());
}
videoPlayer.loopPointReached += OnEpisodeFinished;
Debug.Log($"Playing episode {currentEpisode}");
// Start the UI hide timer
if (hideUICoroutine != null)
{
StopCoroutine(hideUICoroutine);
}
hideUICoroutine = StartCoroutine(HideUIAfterDelay());
}
IEnumerator SyncFMODWithVideo()
{
while (videoPlayer.isPlaying && videoAudioInstance.isValid())
{
videoAudioInstance.getPlaybackState(out PLAYBACK_STATE state);
// Convert video time to milliseconds for FMOD
int targetPosition = (int)(videoPlayer.time * 1000);
videoAudioInstance.setTimelinePosition(targetPosition);
yield return new WaitForSeconds(0.1f);
}
}
void OnEpisodeFinished(VideoPlayer vp)
{
Debug.Log("Episode finished playing");
videoPlayer.loopPointReached -= OnEpisodeFinished;
if (episodeProgressBar != null)
{
episodeProgressBar.value = 1f;
}
StopFMODAudio();
ShowNextEpisodeButton();
}
void UpdateProgressBar()
{
if (videoPlayer.clip != null && episodeProgressBar != null && !isPlayingIntro && !isSeeking)
{
double progress = videoPlayer.time / videoPlayer.clip.length;
episodeProgressBar.value = (float)progress;
}
}
void ShowNextEpisodeButton()
{
Debug.Log($"ShowNextEpisodeButton called. Current episode: {currentEpisode}, Total episodes: {episodes?.Length}");
if (nextEpisodeButton != null)
{
Debug.Log("Next episode button reference exists");
}
else
{
Debug.LogError("Next episode button is null!");
return;
}
if (currentEpisode + 1 < episodes.Length)
{
Debug.Log("There is a next episode available");
nextEpisodeButton.gameObject.SetActive(true);
ShowUI();
// Reset the button fill state
Image buttonImage = nextEpisodeButton.GetComponent<Image>();
if (buttonImage != null)
{
buttonImage.type = Image.Type.Simple;
buttonImage.fillAmount = 1;
}
if (nextEpisodeCoroutine != null)
{
StopCoroutine(nextEpisodeCoroutine);
}
nextEpisodeCoroutine = StartCoroutine(FillNextEpisodeButton());
}
else
{
Debug.Log("No more episodes available");
}
}
IEnumerator FillNextEpisodeButton()
{
Image buttonImage = nextEpisodeButton.GetComponent<Image>();
if (buttonImage != null)
{
buttonImage.type = Image.Type.Filled;
buttonImage.fillMethod = Image.FillMethod.Horizontal;
buttonImage.fillAmount = 0;
float duration = 5f;
float elapsed = 0f;
while (elapsed < duration && nextEpisodeButton.gameObject.activeInHierarchy)
{
elapsed += Time.deltaTime;
buttonImage.fillAmount = Mathf.Clamp01(elapsed / duration);
yield return null;
}
if (nextEpisodeButton.gameObject.activeInHierarchy && !isNextEpisodeAutoPlay)
{
OnNextEpisodeButtonClicked();
}
}
}
void OnNextEpisodeButtonClicked()
{
if (nextEpisodeCoroutine != null)
{
StopCoroutine(nextEpisodeCoroutine);
}
if (nextEpisodeButton != null)
{
nextEpisodeButton.gameObject.SetActive(false);
}
isNextEpisodeAutoPlay = false;
currentEpisode++;
if (currentEpisode < episodes.Length)
{
introSkipped = false;
PlayIntro();
}
else
{
Debug.Log("All episodes finished!");
if (titleText != null)
{
titleText.text = "Series Complete!";
titleText.gameObject.SetActive(true);
}
}
}
void StopFMODAudio()
{
if (videoAudioInstance.isValid())
{
videoAudioInstance.stop(FMOD.Studio.STOP_MODE.IMMEDIATE);
videoAudioInstance.release();
isFMODPlaying = false;
}
if (syncCoroutine != null)
{
StopCoroutine(syncCoroutine);
}
}
// Optional: Method to control FMOD volume
public void SetFMODVolume(float volume)
{
if (videoAudioInstance.isValid())
{
videoAudioInstance.setVolume(Mathf.Clamp01(volume));
}
}
// Optional: Method to set FMOD parameter
public void SetFMODParameter(string parameterName, float value)
{
if (videoAudioInstance.isValid())
{
videoAudioInstance.setParameterByName(parameterName, value);
}
}
Dictionary<double, string> ParseSubtitles(TextAsset subtitleFile)
{
if (subtitleFile == null)
{
return new Dictionary<double, string>();
}
string extension = Path.GetExtension(subtitleFile.name).ToLower();
switch (extension)
{
case ".srt":
return ParseSRT(subtitleFile.text);
case ".vtt":
return ParseVTT(subtitleFile.text);
case ".ass":
return ParseASS(subtitleFile.text);
default:
Debug.LogError("Unsupported subtitle format: " + extension);
return new Dictionary<double, string>();
}
}
Dictionary<double, string> ParseSRT(string subtitleText)
{
var subtitleDict = new Dictionary<double, string>();
string[] blocks = Regex.Split(subtitleText, @"\r?\n\r?\n");
foreach (string block in blocks)
{
if (string.IsNullOrWhiteSpace(block)) continue;
string[] lines = Regex.Split(block.Trim(), @"\r?\n");
if (lines.Length < 3) continue;
string timeLine = lines[1];
if (timeLine.Contains("-->"))
{
string[] timeParts = timeLine.Split(new string[] { "-->" }, System.StringSplitOptions.None);
if (timeParts.Length == 2)
{
double startTime = ParseTimestampSRT(timeParts[0].Trim());
string subtitleLine = "";
for (int i = 2; i < lines.Length; i++)
{
if (i > 2) subtitleLine += " ";
subtitleLine += lines[i];
}
if (!string.IsNullOrEmpty(subtitleLine))
{
subtitleDict[startTime] = subtitleLine;
}
}
}
}
return subtitleDict;
}
Dictionary<double, string> ParseVTT(string subtitleText)
{
var subtitleDict = new Dictionary<double, string>();
string[] lines = Regex.Split(subtitleText, @"\r?\n");
for (int i = 0; i < lines.Length; i++)
{
string line = lines[i].Trim();
if (line.Contains("-->"))
{
string[] timeParts = line.Split(new string[] { "-->" }, System.StringSplitOptions.None);
if (timeParts.Length == 2)
{
double startTime = ParseTimestampVTT(timeParts[0].Trim());
string subtitleLine = "";
for (int j = i + 1; j < lines.Length && !string.IsNullOrWhiteSpace(lines[j]); j++)
{
if (!string.IsNullOrEmpty(subtitleLine)) subtitleLine += " ";
subtitleLine += lines[j].Trim();
}
if (!string.IsNullOrEmpty(subtitleLine))
{
subtitleDict[startTime] = subtitleLine;
}
}
}
}
return subtitleDict;
}
Dictionary<double, string> ParseASS(string subtitleText)
{
var subtitleDict = new Dictionary<double, string>();
string[] lines = Regex.Split(subtitleText, @"\r?\n");
foreach (string line in lines)
{
if (line.StartsWith("Dialogue:"))
{
string[] parts = line.Split(',');
if (parts.Length > 9)
{
double timestamp = ParseTimestampASS(parts[1].Trim());
string subtitleLine = parts[9];
for (int i = 10; i < parts.Length; i++)
{
subtitleLine += "," + parts[i];
}
if (!string.IsNullOrEmpty(subtitleLine))
{
subtitleDict[timestamp] = subtitleLine;
}
}
}
}
return subtitleDict;
}
double ParseTimestampSRT(string timestamp)
{
try
{
string[] timeParts = timestamp.Split(':');
string[] secondsParts = timeParts[2].Split(',');
int hours = int.Parse(timeParts[0]);
int minutes = int.Parse(timeParts[1]);
int seconds = int.Parse(secondsParts[0]);
int milliseconds = int.Parse(secondsParts[1]);
return hours * 3600 + minutes * 60 + seconds + milliseconds / 1000.0;
}
catch
{
return 0;
}
}
double ParseTimestampVTT(string timestamp)
{
try
{
string[] timeParts = timestamp.Split(':');
string[] secondsParts = timeParts[2].Split('.');
int hours = int.Parse(timeParts[0]);
int minutes = int.Parse(timeParts[1]);
int seconds = int.Parse(secondsParts[0]);
int milliseconds = int.Parse(secondsParts[1]);
return hours * 3600 + minutes * 60 + seconds + milliseconds / 1000.0;
}
catch
{
return 0;
}
}
double ParseTimestampASS(string timestamp)
{
try
{
string[] timeParts = timestamp.Split(':');
string[] secondsParts = timeParts[2].Split('.');
int hours = int.Parse(timeParts[0]);
int minutes = int.Parse(timeParts[1]);
int seconds = int.Parse(secondsParts[0]);
int centiseconds = int.Parse(secondsParts[1]);
return hours * 3600 + minutes * 60 + seconds + centiseconds / 100.0;
}
catch
{
return 0;
}
}
void DisplaySubtitle()
{
if (subtitleText == null) return;
double currentTime = videoPlayer.time;
string currentSubtitle = "";
foreach (var kvp in subtitles)
{
if (currentTime >= kvp.Key && currentTime <= kvp.Key + 3)
{
currentSubtitle = kvp.Value;
break;
}
}
if (subtitleText.text != currentSubtitle)
{
subtitleText.text = currentSubtitle;
}
}
void OnDestroy()
{
if (renderTexture != null)
{
renderTexture.Release();
}
if (hideUICoroutine != null)
{
StopCoroutine(hideUICoroutine);
}
if (nextEpisodeCoroutine != null)
{
StopCoroutine(nextEpisodeCoroutine);
}
if (syncCoroutine != null)
{
StopCoroutine(syncCoroutine);
}
StopFMODAudio();
}
void OnValidate()
{
if (videoPlayer == null)
{
videoPlayer = GetComponent<VideoPlayer>();
}
}
}
Videos and audio don’t play - when I use the slicer to move the video the audio glitches by playing the same audio frame on repeat and the video frame is stucked… I have been trying to fix it a long time and still don’t know the reason of this issue… could you please try this script in Unity and fix it for me?