Jack M.
Jack M.

Reputation: 2031

Unity 5.6: ram usage continues to grow

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

Answers (1)

dogiordano
dogiordano

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

Related Questions