1

I'm creating a Gear VR android app that plays a large 30 minute video. I have read that I should not use the StreamingAssets folder, but I don't see any information about what I should be using instead. If I put the video in Assets/videos it is not included in the android build. Should I be using the resources folder or is there another way to include the video? (My project is also on iOS, but I'm currently not having any problems on that platform.)

I have read that the PersistentDataPath folder can be used, but I don't know how to get my video into that folder when from unity. I don't want to copy the video there and have two 600mb files when only one is used.

Programmer
  • 121,791
  • 22
  • 236
  • 328
  • *"I have read that I should not use the StreamingAssets folder"* Where did you read this? – Programmer Aug 09 '17 at 02:43
  • https://forum.unity3d.com/threads/large-videos-not-playing-on-gear-vr-but-do-play-on-google-cardboard-any-ideas.395779/ – Lucas Tabachnick Aug 09 '17 at 03:15
  • I'm using AVPro and in their documentation it says this. "Android - Why doesn’t my huge video file play from StreamingAssets folder? – Lucas Tabachnick Aug 09 '17 at 03:20
  • 1
    Files in the StreamingAssets folder for Android are packed into a JAR file and so before we can play them we must extract them to the apps persistent data folder. Huge files can take a long time to extract and copy and sometimes they could even cause the device to run out of storage space or memory. For really large files were commend placing them the videos in other folders on the device and then referencing the absolute path to that file. This also has the added benefit of not having a copy huge files and wait ages when deploying builds to the Android device. " – Lucas Tabachnick Aug 09 '17 at 03:20
  • I provided you many ways to do this in my answer. It's up you to decide which one to use. – Programmer Aug 09 '17 at 11:10
  • Thank you! I will look into using one of those methods. – Lucas Tabachnick Aug 09 '17 at 21:07
  • You are welcome. Don't forget to accept the answer if it's helpful to you. – Programmer Aug 09 '17 at 21:08
  • No need to panic. Just pick one you think is best for you. Maybe the copy and play or the local server solution.....If you have question about any of the methods then ask it or create a question about that method and I will take a look at it. – Programmer Aug 09 '17 at 22:07

3 Answers3

0

Huge files can take a long time to extract and copy and sometimes they could even cause the device to run out of storage space or memory

This is true but you have to know what you are doing. You can't just copy the file with the WWW API or with just File.Copy/File.WriteAllBytes function.

1.Copy the video in chunks then play it. This removes the out-of memory issue.

bool copyLargeVideoToPersistentDataPath(string videoNameWithExtensionName)
{
    string path = Path.Combine(Application.streamingAssetsPath, videoNameWithExtensionName);

    string persistentPath = Path.Combine(Application.persistentDataPath, "Videos");
    persistentPath = Path.Combine(persistentPath, videoNameWithExtensionName);

    bool success = true;

    try
    {
        using (FileStream fromFile = new FileStream(path, FileMode.Open, FileAccess.Read))
        {
            using (FileStream toFile = new FileStream(persistentPath, FileMode.OpenOrCreate, FileAccess.ReadWrite))
            {
                byte[] buffer = new byte[32 * 1024];
                int bytesRead;
                while ((bytesRead = fromFile.Read(buffer, 0, buffer.Length)) != 0)
                {
                    toFile.Write(buffer, 0, bytesRead);
                }
                Debug.Log("Done! Saved to Dir: " + persistentPath);
                Debug.Log(videoNameWithExtensionName + " Successfully Copied Video to " + persistentPath);
                text.text = videoNameWithExtensionName + " Successfully Copied Video to " + persistentPath;
            }
        }
    }
    catch (Exception e)
    {
        Debug.Log(videoNameWithExtensionName + " Failed To Copy Video. Reason: " + e.Message);
        text.text = videoNameWithExtensionName + " Failed To Copy Video. Reason: " + e.Message;
        success = false;
    }

    return success;
}

I don't want to copy the video there and have two 600mb files when only one is used.

Here are other options you have:

2. The Resources folder. (Not recommended) as this will slow down your game loading time. Worth mentioning. With the new Unity VideoPlayer, you can load video like this:

VideoClip video = Resources.Load("videoFile", typeof(VideoClip)) as VideoClip;

AVPro plugin is not required for this and is no longer needed in Unity.


You may also be able to use the Resources folder with the AVPro plugin if that's what you want.

Just change the video extension to .bytes then load it as bytes. If AVPro plugin can play video as bytes then there you go!

TextAsset txtAsset = (TextAsset)Resources.Load("videoFile", typeof(TextAsset));
byte[] videoFile = txtAsset.bytes;

3.Use AssetBundle to load the video. This was not supported in the past when the new Video API came out but should now be supported.

IEnumerable LoadObject(string path)
{
    AssetBundleCreateRequest bundle = AssetBundle.LoadFromFileAsync(path);
    yield return bundle;

    AssetBundle myLoadedAssetBundle = bundle.assetBundle;
    if (myLoadedAssetBundle == null)
    {
        Debug.Log("Failed to load AssetBundle!");
        yield break;
    }

    AssetBundleRequest request = myLoadedAssetBundle.LoadAssetAsync<GameObject>("VideoFile");
    yield return request;

    VideoClip clip = request.asset as VideoClip;
}

4.Finally, use StreamingAssets folder but instead of copying the video, create a local server with HttpListener and point it to the StreamingAssets path.

Now, connect to with with the new VideoPlayer and play the video. You can find many examples of HttpListener server on stackoverflow. I have done this in the past and it worked.

Programmer
  • 121,791
  • 22
  • 236
  • 328
  • After looking into this I think the real solution is going to be to host the file and download it to the device at the start of the app. Do you know the best way to manage downloading a large file and the location it should be stored? Thank you. – Lucas Tabachnick Aug 11 '17 at 01:04
  • It should be stored to `Application.persistentDataPath/videofolder/`. – Programmer Aug 11 '17 at 04:15
0

I found this as a way to download the file and save it to the PersistantDataPath. http://answers.unity3d.com/questions/322526/downloading-big-files-on-ios-www-will-give-out-of.html

A problem I see is that if the application is paused. (taking the headset off for example.) The download fails. With a 650MB file this will probably happen with basically all users. Do you know how I can keep the download going while unity pauses? I'm also going to make a new post about this.

0

Here is the code I'm going with now.

 void DownloadFile()
{
    loadingBar.gameObject.SetActive(true);
    downloadButton.SetActive(false);
    downloadingFile = true;
    WebClient client = new WebClient();
    client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
    client.DownloadFileCompleted += new System.ComponentModel.AsyncCompletedEventHandler(DownloadFileCompleted);
    client.DownloadFileAsync(new Uri(url), Application.persistentDataPath + "/" + fileName);
}

void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
    double bytesIn = double.Parse(e.BytesReceived.ToString());
    double totalBytes = double.Parse(e.TotalBytesToReceive.ToString());
    double percentage = bytesIn / totalBytes * 100;
    downloadProgressText = "Downloaded " + e.BytesReceived + " of " + e.TotalBytesToReceive;
    downloadProgress = int.Parse(Math.Truncate(percentage).ToString());
    totalBytes = e.TotalBytesToReceive;
    bytesDownloaded = e.BytesReceived;
}

void DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
    if (e.Error == null)
    {
        AllDone();
    }
}

void AllDone()
{
    Debug.Log("File Downloaded");
    FileExists = 1;
}

public void DeleteVideo()
{
    print("Delete File");
    PlayerPrefs.DeleteKey("videoDownloaded");
    FileExists = 0;
    enterButton.SetActive(false);
    downloadButton.SetActive(true);
    File.Delete(Application.persistentDataPath + "/" + fileName);
}