Reputation: 2031
I have made this media player using Unity 5.6 video player and RawImage -component. If I keep it running for +40h straight it will run out of memory and crash. Error logs only indicates that memory is low.
Is there something I could do more memory-efficient in my code or somehow reduce memory consumption?
My test pc has i3 -processor and 4gb ram.
Thanks for advices :)
Player.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Net;
using System.Text.RegularExpressions;
using UnityEngine.Video;
public class Play : MonoBehaviour
{
// Use this for initialization
bool develop = true;
bool web = false;
//UI & Audio
private VideoPlayer videoPlayer;
private VideoSource videoSource;
private AudioSource audioSource;
//Local
private string path = "file://";
private string folder = "C:/medias/";
private WaitForSeconds waitTime;
int arrLength;
int i = 0;
//Web (http)
private string webpath = "http://";
string uri = "www.rtcmagazine.com/files/images/5353/";
string source;
string str;
WWW www;
//Extensions to get
string[] extensions = new[] { ".jpg", ".JPG", ".jpeg", ".JPEG", ".png", ".PNG", ".mp4", ".MP4", ".webm", ".WEBM", ".avi", ".AVI" };
FileInfo[] info;
DirectoryInfo dir;
void Start()
{
//Add VideoPlayer to the GameObject
videoPlayer = gameObject.AddComponent<VideoPlayer>();
//Add AudioSource
audioSource = gameObject.AddComponent<AudioSource>();
//Disable Play on Awake for both Video and Audio
videoPlayer.playOnAwake = true;
audioSource.playOnAwake = true;
if (web) {
string[] fetchFilesCount = GetFiles (webpath+uri);
int getLenght = fetchFilesCount.Length;
info = new FileInfo[getLenght];
int counter = 0;
string[] filesFromWeb = GetFiles (webpath+uri);
foreach (String file in filesFromWeb)
{
info [counter] = new FileInfo(webpath+uri+file);
counter++;
if (counter == filesFromWeb.Length) {
counter = 0;
}
}
} else {
info = new FileInfo[]{};
dir = new DirectoryInfo(@folder);
info = dir.GetFiles().Where(f => extensions.Contains(f.Extension.ToLower())).ToArray();
}
arrLength = info.Length;
Application.runInBackground = true;
StartCoroutine(looper());
}
IEnumerator looper()
{
//Run forever
while (true)
{
if (i == arrLength - 1)
{
if (web) {
int counter = 0;
string[] filesFromWeb = GetFiles (webpath+uri);
foreach (String file in filesFromWeb)
{
info [counter] = new FileInfo(webpath+uri+file);
counter++;
if (counter == filesFromWeb.Length) {
counter = 0;
}
}
} else {
info = dir.GetFiles ().Where (f => extensions.Contains (f.Extension.ToLower ())).ToArray ();
arrLength = info.Length;
}
i = 0;
}
else
{
i++;
}
//Debug.Log(info[i]);
yield return StartCoroutine(medialogic());
int dotIndex = info[i].ToString().LastIndexOf('.');
//string lhs = index < 0 ? source : source.Substring(0,index),
string searchDot = dotIndex < 0 ? "" : source.Substring(dotIndex+1);
int dotPos = Array.IndexOf(extensions, "." + searchDot);
if (dotPos > 5) {
waitTime = new WaitForSeconds (0);
} else {
waitTime = new WaitForSeconds (7);
}
//Wait for 7 seconds
yield return waitTime;
}
}
IEnumerator medialogic()
{
source = info[i].ToString();
if (web) {
if (develop) {
www = new WWW (source);
} else {
www = new WWW (webpath+uri+source);
}
} else {
if (develop) {
str = path + source;
} else {
str = path + folder + source;
}
www = new WWW (str);
}
int index = source.LastIndexOf('.');
//string lhs = index < 0 ? source : source.Substring(0,index),
string rhs = index < 0 ? "" : source.Substring(index+1);
int pos = Array.IndexOf(extensions, "." + rhs);
if (pos > -1 && pos < 6)
{
//Wait for download to finish
yield return www;
GetComponent<RawImage>().texture = www.texture;
}
else if (pos > 5)
{
//videos here
videoPlayer.source = VideoSource.Url;
videoPlayer.url = str;
//Set Audio Output to AudioSource
videoPlayer.audioOutputMode = VideoAudioOutputMode.AudioSource;
//Assign the Audio from Video to AudioSource to be played
videoPlayer.EnableAudioTrack(0, true);
videoPlayer.SetTargetAudioSource(0, audioSource);
//Set video To Play then prepare Audio to prevent Buffering
videoPlayer.Prepare();
while (!videoPlayer.isPrepared)
{
yield return null;
}
//Assign the Texture from Video to RawImage to be displayed
GetComponent<RawImage>().texture = videoPlayer.texture;
//Play Video
videoPlayer.Play();
//Play Sound
audioSource.Play();
//Set loop to true, hack for a freeze bug
videoPlayer.isLooping = true;
//Alternative way to check if video has ended
videoPlayer.loopPointReached += EndReached;
//Debug.Log ("Play started");
while (videoPlayer.isPlaying)
{
//Debug.Log ("playing");
yield return null;
}
//Debug.Log("Done Playing Video");
}
}
void EndReached(UnityEngine.Video.VideoPlayer videoPlayer) {
//Debug.Log("End reached!");
//Play Video
videoPlayer.Stop();
//Play Sound
audioSource.Stop();
}
public static string[] GetFiles(string url)
{
string[] extensions2 = new[] { ".jpg", ".JPG", ".jpeg", ".JPEG", ".png", ".PNG", ".mp4", ".MP4", ".webm", ".WEBM", ".avi", ".AVI" };
List<string> files = new List<string>(500);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
string html = reader.ReadToEnd();
Regex regex = new Regex("<a href=\".*\">(?<name>.*)</a>");
MatchCollection matches = regex.Matches(html);
if (matches.Count > 0)
{
foreach (Match match in matches)
{
if (match.Success)
{
string[] matchData = match.Groups[0].ToString().Split('\"');
foreach (string x in extensions2)
{
if (match.ToString().Contains(x))
{
files.Add(matchData[1]);
}
}
//files.Add(matchData[1]);
}
}
}
}
}
return files.ToArray();
}
public static string[] getFtpFolderItems(string ftpURL)
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpURL);
request.Method = WebRequestMethods.Ftp.ListDirectory;
//You could add Credentials, if needed
//request.Credentials = new NetworkCredential("anonymous", "password");
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
StreamReader reader = new StreamReader(responseStream);
return reader.ReadToEnd().Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
}
}
Upvotes: 0
Views: 867
Reputation: 714
Your code is clearly too long and complex to analyze toroughly and you should profile your application to know where most of the memory goes but I can give you a couple of hints.
Try to remove every single allocation from your loop.
I'm not sure whether there's a way to reuse the same WWW
or WaitForSeconds
objects but you can try to cache and resure them instead of creating new ones every time.
Also, foreach
loops allocate an Enumerator
object. You can convert it to a regular for
loop.
GetFiles
is itself full of allocations. If your url doesn't change often, you can cache the result and reuse it.
info = dir.GetFiles ().Where (f => extensions.Contains (f.Extension.ToLower ())).ToArray ();
I would study this line very closely: ToArray
allocates a new array copying all the elements in the enumerator. GetFiles
again. The lambda expression itself needs an allocation. You can create a regular method and use it instead of the lambda expression. I'm not sure about Where
but I think that allocates a new List too.
You might want to remove all of this.
There are also various string concatenations in your code and those allocate memory too. Consider using a StringBuilder
instead.
Basically, caching is the key to reduce memory allocations in general. Do it wherever possible, whenever needed.
Upvotes: 1