Playing Video in Android mode

hi, I want to play a video in Android mode, I believe there used to be a library to do this but I read that it’s no longer supported.

There is a partial thread here.

But it doesn’t seem to have a final solution.

Are there any code snippets out there that will get me up and running…

Seems like something that a lot of people would have done…

Thanks

Phil

@philspitler===

i dont understand: in the previous forum i have put code examples for that and (if i can remember) everything was ok, though not simple because the question was not only about video but for full screen video, which means using surfaceView. Anyway, in the most simple case, you have 1) to use mediaPlayer from Android and add a videoView 2) give it the path to your video, which can be some url on line, some file on your sd card or some file in your app: code for doing that depends of your choice 3) add if you want some mediaController.
more details here: https://developer.android.com/reference/android/widget/VideoView

Try doing that and if you have any problem put your code: without it is difficult to help.

1 Like

@philspitler

Yes, @akenaton is right … there is code that works. I did it just to learn how … I will post later the complete code I eventually got working.

EDIT: Here’s the code … it is messy, and is still (for me) experimental, but it runs. There are things unrelated to the video itself like getting the SD card details, and drawing a rect in Draw() … for my future use …and the SurfaceHolder.setType method is showing as deprecated. But it does play an mp4 video so hopefully will get you going?!

Good luck …

import android.media.MediaMetadataRetriever;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.app.Activity;
import android.view.ViewGroup;
import android.view.View;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.content.res.Resources;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.content.Context;

AssetFileDescriptor afd;
Context context;
Activity act;
SurfaceView mySurface;
SurfaceHolder mSurfaceHolder;
MediaMetadataRetriever metaRetriever;
MediaPlayer mMediaPlayer;

File[] SDcards ; 
String primarySD, externalSD;

void setup() {
  fullScreen(P2D);
  //size(400,400,P2D);
  act = this.getActivity();
  context = act.getApplicationContext();
  SDcards = context.getExternalFilesDirs(null);
  primarySD = SDcards[0].getPath().split("/Android")[0];
  externalSD = SDcards[1].getPath().split("/Android")[0];
  println("primarySD = " + primarySD) ;
  Looper.prepare();
  mMediaPlayer = new MediaPlayer();
  try {
    afd = context.getAssets().openFd("your_video.mp4"); /// video is in the sketch Data folder
    MediaMetadataRetriever metaRetriever = new MediaMetadataRetriever();
    metaRetriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
    String height = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT); 
    if (int(height) < 2) {
      throw new IOException();
    }
  }
  catch (IllegalArgumentException e) {
    e.printStackTrace();
  }
  catch (IllegalStateException e) {
    e.printStackTrace();
  } 
  catch (IOException e) {
    e.printStackTrace();
  }

  mySurface = new SurfaceView(act);
  mySurface.setZOrderOnTop(true) ;
  mSurfaceHolder = mySurface.getHolder();
  mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
  mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
    //<a href="/two/profile/Override">@Override</a>
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
      mMediaPlayer.setDisplay(surfaceHolder);
    }

    // <a href="/two/profile/Override">@Override</a>
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i2, int i3) {
      mMediaPlayer.setDisplay(surfaceHolder);
    }

    // <a href="/two/profile/Override">@Override</a>
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
    }
  }
  );
  startVideo();
}

void startVideo() {
  act.runOnUiThread(new Runnable() {
    public void run() {
      try {
        mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
        mSurfaceHolder = mySurface.getHolder();
        mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        mMediaPlayer.prepare();
        //        act.addContentView(mySurface, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT));
        act.addContentView(mySurface, new ViewGroup.LayoutParams(400, 400));
        if (mMediaPlayer.isPlaying() == false) {
          mMediaPlayer.start();
        }
      }
      catch (IllegalArgumentException e) {
        e.printStackTrace();
      }
      catch (IllegalStateException e) {
        e.printStackTrace();
      } 
      catch (IOException e) {
        e.printStackTrace();
      }
    }
  }
  );
};

void draw() {
  rect(mouseX,mouseY,40,40);
}

void onPause() {
  if (mMediaPlayer!=null) {
    mMediaPlayer.release();
    mMediaPlayer = null;
  }
  super.onPause() ;
}

void onStop() {
  if (mMediaPlayer!=null) {
    mMediaPlayer.release();
    mMediaPlayer = null;
  }
  super.onStop() ;
}

void onDestroy() {
  if (mMediaPlayer!=null) {
    mMediaPlayer.release();
    mMediaPlayer = null;
  }
  super.onDestroy() ;
}

void onResume() {
  super.onResume() ;
}
1 Like

Great, I’ll re-read the old thread as I must have missed something thanks.

Phil

@philspitler===

i got a look to the post; everything is ok and i am sure that it works; yet, as i have told to you, this code is for using videos on the sdcard and it uses surface view (you can also do the same with textureview); if you want to use videos that are in your data or assets folder you have to change the code (that is not very complicated) and if you want only the videoview the code could be more simple.

Hi, I tried the code and got a weird error about a theme ?

See below.

Any thoughts?

Thanks,

Phil

FATAL EXCEPTION: main
Process: processing.test.androidvideo_v01, PID: 9266
java.lang.RuntimeException: Unable to start activity ComponentInfo{processing.test.androidvideo_v01/processing.test.androidvideo_v01.MainActivity}: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2325)
	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)
	at android.app.ActivityThread.access$800(ActivityThread.java:151)
	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
	at android.os.Handler.dispatchMessage(Handler.java:102)
	at android.os.Looper.loop(Looper.java:135)
	at android.app.ActivityThread.main(ActivityThread.java:5254)
	at java.lang.reflect.Method.invoke(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:372)
	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:920)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:715)
Caused by: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
	at android.support.v7.app.AppCompatDelegateImplV9.createSubDecor(AppCompatDelegateImplV9.java:354)
	at android.support.v7.app.AppCompatDelegateImplV9.ensureSubDecor(AppCompatDelegateImplV9.java:323)
	at android.support.v7.app.AppCompatDelegateImplV9.setContentView(AppCompatDelegateImplV9.java:293)
	at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:149)
	at processing.test.androidvideo_v01.MainActivity.onCreate(MainActivity.java:21)
	at android.app.Activity.performCreate(Activity.java:5990)
	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106)
	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2278)
	... 10 more

I fixed that issue by adding this line to my manifest.xml

android:theme="@style/Theme.AppCompat.Light"

I have one mp4 file in my data folder.

But now I’m getting a different error…

FATAL EXCEPTION: GLThread 538
Process: processing.test.androidvideo_v01, PID: 12079
java.lang.ArrayIndexOutOfBoundsException: length=1; index=1
	at processing.test.androidvideo_v01.AndroidVideo_v01.setup(AndroidVideo_v01.java:69)
	at processing.core.PApplet.handleDraw(PApplet.java:1846)
	at processing.opengl.PSurfaceGLES$RendererGLES.onDrawFrame(PSurfaceGLES.java:264)
	at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1522)
	at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1239)

And my code looks like this.

import android.media.MediaMetadataRetriever;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.app.Activity;
import android.view.ViewGroup;
import android.view.View;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.content.res.Resources;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.content.Context;

AssetFileDescriptor afd;
Context context;
Activity act;
SurfaceView mySurface;
SurfaceHolder mSurfaceHolder;
MediaMetadataRetriever metaRetriever;
MediaPlayer mMediaPlayer;

File[] SDcards ; 
String primarySD, externalSD;

void setup() {
  fullScreen(P2D);
  //size(400,400,P2D);
  act = this.getActivity();
  context = act.getApplicationContext();
  SDcards = context.getExternalFilesDirs(null);
  primarySD = SDcards[0].getPath().split("/Android")[0];
  externalSD = SDcards[1].getPath().split("/Android")[0];
  println("primarySD = " + primarySD) ;
  Looper.prepare();
  mMediaPlayer = new MediaPlayer();
  try {
    afd = context.getAssets().openFd("video1.mp4"); /// video is in the sketch Data folder
    MediaMetadataRetriever metaRetriever = new MediaMetadataRetriever();
    metaRetriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
    String height = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT); 
    if (int(height) < 2) {
      throw new IOException();
    }
  }
  catch (IllegalArgumentException e) {
    e.printStackTrace();
  }
  catch (IllegalStateException e) {
    e.printStackTrace();
  } 
  catch (IOException e) {
    e.printStackTrace();
  }

  mySurface = new SurfaceView(act);
  mySurface.setZOrderOnTop(true) ;
  mSurfaceHolder = mySurface.getHolder();
  mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
  mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
    //<a href="/two/profile/Override">@Override</a>
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
      mMediaPlayer.setDisplay(surfaceHolder);
    }

    // <a href="/two/profile/Override">@Override</a>
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i2, int i3) {
      mMediaPlayer.setDisplay(surfaceHolder);
    }

    // <a href="/two/profile/Override">@Override</a>
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
    }
  }
  );
  startVideo();
}

void startVideo() {
  act.runOnUiThread(new Runnable() {
    public void run() {
      try {
        mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
        mSurfaceHolder = mySurface.getHolder();
        mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        mMediaPlayer.prepare();
        //        act.addContentView(mySurface, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT));
        act.addContentView(mySurface, new ViewGroup.LayoutParams(400, 400));
        if (mMediaPlayer.isPlaying() == false) {
          mMediaPlayer.start();
        }
      }
      catch (IllegalArgumentException e) {
        e.printStackTrace();
      }
      catch (IllegalStateException e) {
        e.printStackTrace();
      } 
      catch (IOException e) {
        e.printStackTrace();
      }
    }
  }
  );
};

void draw() {
  rect(mouseX,mouseY,40,40);
}

void onPause() {
  if (mMediaPlayer!=null) {
    mMediaPlayer.release();
    mMediaPlayer = null;
  }
  super.onPause() ;
}

void onStop() {
  if (mMediaPlayer!=null) {
    mMediaPlayer.release();
    mMediaPlayer = null;
  }
  super.onStop() ;
}

void onDestroy() {
  if (mMediaPlayer!=null) {
    mMediaPlayer.release();
    mMediaPlayer = null;
  }
  super.onDestroy() ;
}

void onResume() {
  super.onResume() ;
}

@philspitler===
the error is with the theme= change it in the Manifest.

@philspitler===

sorry! i have read the first post and answered without reading next one! - As for the second error it s difficult to help without more details: yet it claims about an arrayoutof bounds and i guess that this is caused by your code about SD cards; is your println() ok???

The code fails before the println()

I am not using any SD cards in the project as my mp4 file is my data folder.

I can look at stripping out all the SD card stuff.

Thanks

Phil

@philspitler ===

that is exactly what i guessed: your SDCards array returns only the primary card and it is not so easy to get the second one; try the code without these lines about SDcards and tell me what happens…

Getting rid of the SD code worked great and everything seems to be working.

onDestroy()
onStop()

Curious if there is any reference to what these functions do.

Some of the functions seem to have the same code inside them.

Thanks for the help.

Phil

void onPause() {
  if (mMediaPlayer!=null) {
    mMediaPlayer.release();
    mMediaPlayer = null;
  }
  super.onPause() ;
}

void onStop() {
  if (mMediaPlayer!=null) {
    mMediaPlayer.release();
    mMediaPlayer = null;
  }
  super.onStop() ;
}

void onDestroy() {
  if (mMediaPlayer!=null) {
    mMediaPlayer.release();
    mMediaPlayer = null;
  }
  super.onDestroy() ;
}

void onResume() {
  super.onResume() ;
}

What I’m actually trying to do is have the video stop (and show a black screen) when I tap the screen (then start again on another tap).

Seems like the video is over the top of my current draw function though. I tried adjusting the z order

mySurface.setZOrderOnTop(true) ;

to false but then I didn’t see the video at all.

Any thoughts?

Thanks,

Phil

@philspitler===
these functions are called automatically and according to the fragment lifecycle one after the other but not all in any case: that depends of the os, its needs for other app and what your app is doing: onDestroy() for example is called if the os needs to get free of your app because another one has been launched. Anyway it s quite always a good idea to write all of them, especially when you app uses resources which the user could want to use: video, sound, cam, mic (and generally speaking any mediaplayer stuff). As for the code, you are right, it is redundant to release the player three times!
As for the other point, do you want that the whole screen becomes (on tap) black or only space occupied by the video?

Thanks for the info

The video is playing full screen and I would like to use the whole screen as start / stop button.

Cheers

Phil

@philspitler

I was just trying things with that code … I knew it was a mess!