Search Unity

FileStream.Position setting on files larger than 2gb

Discussion in 'Android' started by mobiusmedia, Sep 21, 2017.

  1. mobiusmedia

    mobiusmedia

    Joined:
    Aug 14, 2017
    Posts:
    19
    Hi guys,

    I am unsure if I should report this as a bug as I believe it's technically a mono problem. See: https://bugzilla.xamarin.com/show_bug.cgi?id=17128

    In my application, everything works fine in windows but as soon as I deploy to android, seeking to anything over the 2gb limit causes the following exception to be thrown:

    IOException: Win32 IO returned 25. Path: /storage/emulated/0/Videos/MyVideo.mp4
    at System.IO.FileStream.Seek (Int64 offset, SeekOrigin origin) [0x00000] in <filename unknown>:0
    at System.IO.FileStream.set_Position (Int64 value) [0x00000] in <filename unknown>:0

    Is there any recommended work around for this issue? Must I write a native plugin or use the native android objects provided as part of the Unity API?

    Also, should I report this as a bug?

    Thanks
     
  2. Tomas1856

    Tomas1856

    Unity Technologies

    Joined:
    Sep 21, 2012
    Posts:
    3,918
    Hi,

    does the same happens when you switch scripting backend to il2cpp?
     
  3. mobiusmedia

    mobiusmedia

    Joined:
    Aug 14, 2017
    Posts:
    19
    Hi @Tomas1856

    Thanks for getting back to me. The issue still occurs with the il2cpp backend.

    However, I have managed to get around this by using the AndroidJavaClass/AndroidJavaObject objects to access native Java FileStream functionality, so this is almost certainly an implementation problem. Most likely to do with 32 bit integer overflow.

    Here is my implementation that works fine with files over 2gb (sorry it doesn't have all the functions, I just implemented what I needed).

    Code (CSharp):
    1. #if UNITY_ANDROID
    2. using System;
    3. using UnityEngine;
    4.  
    5. public class JavaFileStream : IDisposable
    6. {
    7.     AndroidJavaObject _fileChannel;
    8.     AndroidJavaObject _randomAccessFile;
    9.     AndroidJavaClass _byteBufferClass;
    10.  
    11.     /// <summary>
    12.     /// Set the current position in the stream.
    13.     /// </summary>
    14.     public long Position
    15.     {
    16.         set
    17.         {
    18.             _fileChannel = _fileChannel.Call<AndroidJavaObject>("position", value);
    19.         }
    20.     }
    21.  
    22.     /// <summary>
    23.     /// Get the length of the file in bytes.
    24.     /// </summary>
    25.     public long Length
    26.     {
    27.         get
    28.         {
    29.             return _fileChannel.Call<long>("size");
    30.         }
    31.     }
    32.  
    33.     public JavaFileStream(string filePath)
    34.     {
    35.         filePath = filePath.Replace('\\', '/');
    36.      
    37.         _randomAccessFile = new AndroidJavaObject("java.io.RandomAccessFile", filePath, "rw");
    38.         _fileChannel = _randomAccessFile.Call<AndroidJavaObject>("getChannel");
    39.         _byteBufferClass = new AndroidJavaClass("java.nio.ByteBuffer");
    40.     }
    41.  
    42.     /// <summary>
    43.     /// Read the bytes from the stream into the buffer. Will read the amount of bytes of the length of the buffer.
    44.     /// Note that offset and length are ignored, and are the same only so that function signatures need not change between implementations.
    45.     /// </summary>
    46.     /// <param name="buffer">The byte buffer to copy the bytes to. Whose length is used to dictate the read length</param>
    47.     /// <param name="offset">Ignored, here for matching method signature purposes.</param>
    48.     /// <param name="length">Ignored, here for matching method signature purposes.</param>
    49.     /// <returns>An int indicating the amount of bytes read. Can be less than buffer.Length if at the end of the stream.</returns>
    50.     public int Read(byte[] buffer, int offset, int length)
    51.     {
    52.         var byteBufferObject = _byteBufferClass.CallStatic<AndroidJavaObject>("wrap", buffer);
    53.         var bytesRead = _fileChannel.Call<int>("read", byteBufferObject);
    54.         // loses the reference here... kinda sucks that we have to allocate the byte array twice... (probably a way to maintain the ptr with JNI?)
    55.         var readBytes = byteBufferObject.Call<byte[]>("array");
    56.         Array.Copy(readBytes, buffer, readBytes.Length);    
    57.         return bytesRead;
    58.     }
    59.  
    60.     /// <summary>
    61.     /// Write the bytes contained in the buffer to the stream. offset and length params are ignored, and only serve to keep the same function signature between implementations.
    62.     /// </summary>
    63.     /// <param name="buffer">The byte buffer to write to the stream. Whose length is used to dictate the write length. If the stream is not long enough, it will be extended to add the bytes on.</param>
    64.     /// <param name="offset">Ignored, here for matching method signature purposes.</param>
    65.     /// <param name="length">Ignored, here for matching method signature purposes.</param>
    66.     /// <returns>An int indicating the amount of bytes written to the stream.</returns>
    67.     public int Write(byte[] buffer, int offset, int length)
    68.     {
    69.         var byteBufferObject = _byteBufferClass.CallStatic<AndroidJavaObject>("wrap", buffer);
    70.         return _fileChannel.Call<int>("write", byteBufferObject);
    71.     }
    72.  
    73.     /// <summary>
    74.     /// Set the length of the stream, useful for truncation.
    75.     /// </summary>
    76.     /// <param name="newLength"></param>
    77.     public void SetLength(long newLength)
    78.     {
    79.         _fileChannel = _fileChannel.Call<AndroidJavaObject>("truncate", newLength);
    80.     }
    81.  
    82.     public void Dispose()
    83.     {
    84.         _randomAccessFile.Call("close");
    85.         _fileChannel.Call("close");
    86.         _randomAccessFile.Dispose();
    87.         _fileChannel.Dispose();
    88.     }
    89. }
    90. #endif
     
    Last edited: Sep 22, 2017
  4. Tomas1856

    Tomas1856

    Unity Technologies

    Joined:
    Sep 21, 2012
    Posts:
    3,918
    Could you please submit a bug report, so our developers can fix this?

    Glad, you have a workaround.

    Thank you!
     
  5. Tomas1856

    Tomas1856

    Unity Technologies

    Joined:
    Sep 21, 2012
    Posts:
    3,918
  6. conigliocattivo

    conigliocattivo

    Joined:
    May 14, 2013
    Posts:
    10
    Hi @Tomas1856 !

    Is there any update on this issue? I have the same problem as mobiusmedia and I tried his implementation but it's very slow for my case. Has it been solved in new Unity version? (I am running 2017.4.14f1).

    Thanks,
    Antonio