Search Unity

Proper way of updating a texture outside Unity

Discussion in 'Android' started by xucian, Nov 5, 2016.

  1. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hi.
    I want to use to progressively stream videos from urls, but display the video in Unity3D.
    I first tested it with a surfaceview in a native android app and it works brilliantly.
    However, I need this functionality to be contained into an aar library and use it as a plugin for Unity.
    I am VERY close to make it work (or so it seems), but I know my lack of OpenGL knowledge is not letting me advance.
    Should the texture be created in Unity-side or in the plugin-side?
    When should it be re-created and how?
    When should it be destroyed and how?

    I tried several combinations and only a black texture is shown. Audio works in each case.
    The code is a mess, as now I'm still in the "discovering" stage and I'm trying everything I can think of.
    I didn't delete all the comments, so you can see what other approaches I used. I also put some questions in the comments.
    I know the solution needs unity+exoplayer+opengl+android knowledge at the same time, so I also asked this on SO and linked the posts to each other. Hopefully, I can combine the answers I get here with those ones and come up with the solution.

    Android side:
    Code (CSharp):
    1. public class ExoPlayerForUnity {
    2.     static SurfaceTexture surfaceTexture;
    3.     static Surface surface;
    4.  
    5.     static Context getUnityContext() {
    6.         try {
    7.             Class<?> unityPlayerClass = Class.forName("com.unity3d.player.UnityPlayer");
    8.             Field currentActivity = unityPlayerClass.getField("currentActivity");
    9.             Object o = currentActivity.get(null);
    10.             return (Context)o;
    11.         } catch (ClassNotFoundException e) {
    12.             e.printStackTrace();
    13.         } catch (NoSuchFieldException e) {
    14.             e.printStackTrace();
    15.         } catch (IllegalAccessException e) {
    16.             e.printStackTrace();
    17.         }
    18.  
    19.         return null;
    20.     }
    21.  
    22.  
    23.     public static int init( String uriString) {
    24.  
    25.         Uri videoURI = Uri.parse(uriString);
    26.  
    27.         final Activity unityContext = (Activity)getUnityContext();
    28.  
    29.         final Handler handler = new Handler();
    30. //        BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
    31. //        TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveVideoTrackSelection.Factory(bandwidthMeter);
    32. //        TrackSelection.Factory videoTrackSelectionFactory = new FixedTrackSelection.Factory();
    33. //        TrackSelector trackSelector = new DefaultTrackSelector(mainHandler, videoTrackSelectionFactory);
    34.         TrackSelector trackSelector = new DefaultTrackSelector(handler, null);
    35.         LoadControl loadControl = new DefaultLoadControl();
    36.         final SimpleExoPlayer player = ExoPlayerFactory.newSimpleInstance(unityContext, trackSelector, loadControl, null, false);
    37.  
    38.         DefaultBandwidthMeter defBandwidthMeter = new DefaultBandwidthMeter();
    39.         DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(unityContext,
    40.                 Util.getUserAgent(unityContext, "yourApplicationName"), defBandwidthMeter);
    41.         ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
    42.         MediaSource videoSource = new ExtractorMediaSource(videoURI,
    43.                 dataSourceFactory, extractorsFactory, new Handler(), new ExtractorMediaSource.EventListener() {
    44.             @Override
    45.             public void onLoadError(IOException error) {
    46.                 error.printStackTrace();
    47.             }
    48.         });
    49.  
    50.  
    51.         // Maybe useful?: http://stackoverflow.com/questions/33324753/how-to-use-unity-createexternaltexture-on-android
    52.         int[] textures = new int[1];
    53.         GLES20.glGenTextures(1, textures, 0);
    54.         final int texture = textures[0];
    55.  
    56. //        //GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    57. //        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
    58. //        //GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, 1024, 512, 0, GLES20.GL_RGB, GLES20.GL_UNSIGNED_BYTE, null);
    59. //        GLES20.glTexParameteri( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR );
    60. //        GLES20.glTexParameteri( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR );
    61. //        GLES20.glTexParameteri( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE );
    62. //        GLES20.glTexParameteri( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE );
    63. //        GLES20.glBindTexture( GLES20.GL_TEXTURE_2D, 0 );
    64.  
    65.         GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texture);
    66.         GLES20.glTexParameteri( GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR );
    67.         GLES20.glTexParameteri( GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR );
    68.         GLES20.glTexParameteri( GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE );
    69.         GLES20.glTexParameteri( GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE );
    70.         GLES20.glBindTexture( GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0 );
    71.  
    72.  
    73.  
    74.         // TODO work with attachToGLContext maybe?
    75.         surfaceTexture = new SurfaceTexture(texture);
    76.  
    77.         surfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {
    78.             @Override
    79.             public void onFrameAvailable(final SurfaceTexture sf) {
    80.                 handler.post(new Runnable() {
    81.                     @Override
    82.                     public void run() {
    83.                         // Should I use this or update it in Unity-side with tex.UpdateTextureExternal(tex.GetNativeTexturePtr()) ?
    84.                         //GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texture);
    85.                         //sf.updateTexImage();
    86.                         //GLES20.glBindTexture( GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0 );
    87.                     }
    88.                 });
    89.             }
    90.         });
    91.  
    92.         surface = new Surface(surfaceTexture);
    93.  
    94.         Timer t = new Timer();
    95.         t.schedule(
    96.                 new TimerTask() {
    97.                     @Override
    98.                     public void run() {
    99.                         Log.v("ExoPlayerForUnity", "buff: " + player.getBufferedPercentage());
    100.                     }
    101.                 }, 3000, 1000
    102.         );
    103.  
    104.         player.setVideoSurface(surface);
    105.         player.prepare(videoSource);
    106.         player.setPlayWhenReady(true);
    107.  
    108.         return texture;
    109.     }
    110. }





    Unity side:
    Code (CSharp):
    1. public class ExoTestPlugin : MonoBehaviour
    2. {
    3.     new Renderer renderer;
    4.     Texture2D rt;
    5.     //RenderTexture rt; // maybe this?
    6.     AndroidJavaClass jc;
    7.     int texName;
    8.     float curScale = 0f;
    9.  
    10.     void init()
    11.     {
    12.         renderer = GetComponent<Renderer>();
    13.         Camera.onPreRender += OnPreRender2;
    14.  
    15.         jc = new AndroidJavaClass("com.thefallengames.exoplayerforunity.ExoPlayerForUnity");
    16.  
    17.         texName = jc.CallStatic<int>("init", 0, "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4");
    18.    
    19.  
    20.         //rt = new Texture2D(640, 360, TextureFormat.RGBA32, true, true);
    21.         //rt = new Texture2D(640, 360, TextureFormat.ETC2_RGBA8, true, true);
    22.         //rt = new RenderTexture(640, 360, 24, RenderTextureFormat.Default, RenderTextureReadWrite.Default);
    23.         //rt.Create();
    24.  
    25.         rt = Texture2D.CreateExternalTexture(1024, 512, TextureFormat.ETC_RGB4, true, true, (IntPtr)texName);
    26.  
    27.         renderer.material.mainTexture = rt;
    28.     }
    29.  
    30.     void Update () {
    31.  
    32.         // Here I'm just starting the playback on first tap and then toggling between 2x and 1x scale on any subsequent taps
    33.         if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Ended)
    34.         {
    35.             if (curScale == 0f)
    36.             {
    37.                 curScale = 1f;
    38.                 init();
    39.  
    40.                 return;
    41.             }
    42.  
    43.             if (curScale == 2f)
    44.                 curScale = .5f;
    45.             else
    46.                 curScale = 2f;
    47.             transform.localScale *= curScale;
    48.         }
    49.     }
    50.  
    51.     void OnPreRender2(Camera cam)
    52.     {
    53.         if (jc == null) // not inited
    54.             return;
    55.  
    56.         if (UnityEngine.Random.Range(0, 60) != 0) // updating ~once per second only, just for debugging
    57.             return;
    58.  
    59.         rt.UpdateExternalTexture((IntPtr)texName);
    60.     }
    61.  
    62.  
    63.  
    64. }
     
    Last edited: Nov 7, 2016
  2. gilgil28

    gilgil28

    Joined:
    Feb 3, 2016
    Posts:
    9
    any luck with that?
     
  3. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Nope
     
  4. gilgil28

    gilgil28

    Joined:
    Feb 3, 2016
    Posts:
    9
    I think it has to do with GL_TEXTURE_EXTERNAL_OES and unity. It has to use that external source somehow