Running videos on android

I have used Movie in Processing for Windows from processing.video.* but cannot use it in android.

I have tried using FileDescriptor and getAssets etc with the video in data or other folder (located via Environment.getExternalStorageDirectory()) but I just get a null pointer, although it is looking in the correct folder.

Any ideas where the problem lies? I have searched on the web but none of the solutions work. The manifest has the correct permissions.

@paturn === it works fine with AFD and mediaPlayer
can you put the code you are using?

@akenaton
Code as follows :-
import android.media.MediaMetadataRetriever;
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;
import android.widget.FrameLayout;
import android.R;
import android.content.*;
import android.view.Gravity;
import android.view.ViewGroup.LayoutParams;
import android.os.Environment;

AssetFileDescriptor afd;
Context context;
Activity act;
SurfaceView mySurface;
SurfaceHolder mSurfaceHolder;
MediaMetadataRetriever metaRetriever;
MediaPlayer mp;
FrameLayout fl;
String pathstr = Environment.getExternalStorageDirectory();.

void setup() {
size(10, 10);
Looper.prepare();
try {
afd = context.getAssets().openFd(pathstr+“ConDrift.mp4”);
MediaMetadataRetriever metaRetriever = new MediaMetadataRetriever();
metaRetriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
String height = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
}
catch (IOException e) {
e.printStackTrace();
}
mp = new MediaPlayer();
try {
mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
mp.prepare();
}
catch (IOException e) {
e.printStackTrace();
}
startVideo();
}

void startVideo() {
act.runOnUiThread(new Runnable() {
public void run() {

  if (mp.isPlaying() == false) {
    mp.start();
  }
}

}
);
}

void draw() {
}

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

@Override
public void onStart() {
act = this.getActivity();
context = act.getApplicationContext();
mySurface = new SurfaceView(act);
mSurfaceHolder = mySurface.getHolder();
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
mp.setDisplay(surfaceHolder);
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i2, int i3) {
mp.setDisplay(surfaceHolder);
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
}
}
);
mySurface.bringToFront();
//mySurface.setX(100);
//mySurface.setY(100);
fl = (FrameLayout)act.findViewById(R.id.content);
FrameLayout.LayoutParams params1 = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, Gravity.CENTER);
fl.addView(mySurface);
}
pathstr shows correct folder in console, and mp4 file us in that folder, but error says file not found.

@paturn === ill’give a look at that to morrow; it has to run

@paturn === i gave a look to this old code and made some modifs; it runs; you have ony to put your video in the data folder (and change the name, of course; now i have another solution with a videoView; if you want i can put the code.Tested with MM and Nougat.

import android.media.MediaMetadataRetriever;
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.MediaPlayer;
import android.content.res.Resources;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.content.Context;
import android.widget.FrameLayout;
import android.content.*;
import android.view.Gravity;
import android.view.ViewGroup.LayoutParams;
import android.os.Environment;
import android.content.pm.PackageManager;
import android.os.Build;
import android.Manifest;
import android.os.Build;
////////////////////////////////////////////////
int MyVersion = Build.VERSION.SDK_INT;
AssetFileDescriptor afd;
Context context;
Activity act;
SurfaceView mySurface;
SurfaceHolder mSurfaceHolder;
MediaMetadataRetriever metaRetriever;
MediaPlayer mp;
FrameLayout fl;
boolean surfacecreated = false;///new, in order to avoid taht the mp is created but not the surface
//String pathstr = Environment.getExternalStorageDirectory();////i put the video in the data folder

void setup() {
size(10, 10);// to show that the surface is created
permissions();///new, normally it is notneeded, yet i am prudent with that (after lollypop they are asked on runtime and not only through the Manifest)
Looper.prepare();//as you modify the main ui thread
}


void permissions(){
if (MyVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
                if (!permissionsDejaAccordees()) {
                    demandePermissionParticuliere();
                }
};  
};

private boolean permissionsDejaAccordees() {
/// in some cases i have seen that an error is fired if the permissision is allready granted
    int result = act.checkSelfPermission( Manifest.permission.WRITE_EXTERNAL_STORAGE);
    if (result == PackageManager.PERMISSION_GRANTED) {
      println("permission déjà accordée");
        return true;
    } else {
      println("je demande particulière");
        return false;
    }
};

private void demandePermissionParticuliere()  {
        
        act.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},101);////you can also ask only for read but...
};

public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    switch (requestCode) {
        case 101:
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
               println("permissions accordées");
            } else {
                println("permissions not granted");
            }
            break;
        default:
            act.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}
void prepareMP(){
 try {
afd = context.getAssets().openFd("who.mp4");
MediaMetadataRetriever metaRetriever = new MediaMetadataRetriever();
metaRetriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
String height = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
println("la hauteur2 de la video est=="+ height);///in order to be sure that the video was found and also, if needed to work with the height or the width of the video and the surface (scaling)
}
catch (IOException e) {
e.printStackTrace();
}
mp = new MediaPlayer();
try {
mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); 
mp.prepare();
}catch (IOException e) {
e.printStackTrace();
}

if(surfacecreated){
startVideo();
}
};

void startVideo() {
act.runOnUiThread(new Runnable() {
public void run() {

  if (mp.isPlaying() == false) {
    mp.start();
  }
}

}
);
}

void draw() {
}

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

@Override
public void onStart() {
act = this.getActivity();
context = act.getApplicationContext();
prepareMP();//new, because it happens that onStart is performed before settings() and mp is not //instantiated
mySurface = new SurfaceView(act);
mSurfaceHolder = mySurface.getHolder();
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
  surfacecreated = true;//new, i am very prudent....
  println("la surface a été créé");
  if(mp !=null){
mp.setDisplay(surfaceHolder);
startVideo();
  }else{
    println("le mediaplayer n'est pas ok");
    prepareMP();
    mp.setDisplay(surfaceHolder);
    startVideo();
  
}
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i2, int i3) {
mp.setDisplay(surfaceHolder);
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
}
}
);
mySurface.bringToFront();
//mySurface.setX(100);
//mySurface.setY(100);
fl = (FrameLayout)act.findViewById(R.id.content);
FrameLayout.LayoutParams params1 = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, Gravity.CENTER);
fl.addView(mySurface);
}

Thank you akenaton.

I’ll try it tomorrow.

When I’ve written programs for video in Eclipse I’ve always used just READ EXTERNAL STORAGE permission. Does Processing require extra pemissions?

@paturn ==== write is more potent: if you can write you can read!!!

@akenaton
Result is the following error

Found a { that’s missing a matching }

That is all. One line. Not even a list or an explanation of what “a” is.
I must be missing something which your code requires and my previous code did not require.

Any ideas?

Have you been able to run it? If you are, would you share it? I also have problems with the video :slight_smile:

@clay21

No, please see above result.

Previous videos on android using Eclipse work OK but I can’t get a video to run on Processing for Android.

See here the answer to @clay21 I gave.

I get the surface view from this, but I still get the error message file not found. This applies to the data folder or any folder located from the Environment source.

Whenever I use any other of my android apps (via Eclipse) the correct video file is found.

I assume my settings in Processing are not correct. Any suggestions?

I’ve removed the reference to the GStreamer library but it still gives the same error of file not found.

Is there a different video library that I should be using?

@paturn === i heve verified, the code is ok; there wa probably a typo with cut && copy; i put the code again: dont forget the permission and put the orientation according to your video.


import android.R;
import android.media.MediaMetadataRetriever;
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.MediaPlayer;
import android.content.res.Resources;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.content.Context;
import android.widget.FrameLayout;
import android.content.*;
import android.view.Gravity;
import android.view.ViewGroup.LayoutParams;
import android.os.Environment;
import android.content.pm.PackageManager;
import android.os.Build;
import android.Manifest;
import android.os.Build;
////////////////////////////////////////////////
int MyVersion = Build.VERSION.SDK_INT;
AssetFileDescriptor afd;
Context context;
Activity act;
SurfaceView mySurface;
SurfaceHolder mSurfaceHolder;
MediaMetadataRetriever metaRetriever;
MediaPlayer mp;
FrameLayout fl;
boolean surfacecreated = false;///new, in order to avoid taht the mp is created but not the surface
//String pathstr = Environment.getExternalStorageDirectory();////i put the video in the data folder

void setup() {
size(10, 10);// to show that the surface is created
permissions();///new, normally it is notneeded, yet i am prudent with that (after lollypop they are asked on runtime and not only through the Manifest)
Looper.prepare();//as you modify the main ui thread
}


void permissions(){
if (MyVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
                if (!permissionsDejaAccordees()) {
                    demandePermissionParticuliere();
                }
};  
};

private boolean permissionsDejaAccordees() {
/// in some cases i have seen that an error is fired if the permissision is allready granted
    int result = act.checkSelfPermission( Manifest.permission.WRITE_EXTERNAL_STORAGE);
    if (result == PackageManager.PERMISSION_GRANTED) {
      println("permission déjà accordée");
        return true;
    } else {
      println("je demande particulière");
        return false;
    }
};

private void demandePermissionParticuliere()  {
        
        act.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},101);////you can also ask only for read but...
};

public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    switch (requestCode) {
        case 101:
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
               println("permissions accordées");
            } else {
                println("permissions not granted");
            }
            break;
        default:
            act.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}
void prepareMP(){
 try {
afd = context.getAssets().openFd("who.mp4");
MediaMetadataRetriever metaRetriever = new MediaMetadataRetriever();
metaRetriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
String height = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
println("la hauteur2 de la video est=="+ height);///in order to be sure that the video was found and also, if needed to work with the height or the width of the video and the surface (scaling)
}
catch (IOException e) {
e.printStackTrace();
}
mp = new MediaPlayer();
try {
mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); 
mp.prepare();
}catch (IOException e) {
e.printStackTrace();
}

if(surfacecreated){
startVideo();
}
};

void startVideo() {
act.runOnUiThread(new Runnable() {
public void run() {

  if (mp.isPlaying() == false) {
    mp.start();
  }
}

}
);
}

void draw() {
}

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

@Override
public void onStart() {
act = this.getActivity();
context = act.getApplicationContext();
prepareMP();//new, because it happens that onStart is performed before settings() and mp is not //instantiated
mySurface = new SurfaceView(act);
mSurfaceHolder = mySurface.getHolder();
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
  surfacecreated = true;//new, i am very prudent....
  println("la surface a été créé");
  if(mp !=null){
mp.setDisplay(surfaceHolder);
startVideo();
  }else{
    println("le mediaplayer n'est pas ok");
    prepareMP();
    mp.setDisplay(surfaceHolder);
    startVideo();
  
}
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i2, int i3) {
mp.setDisplay(surfaceHolder);
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
}
}
);
mySurface.bringToFront();
//mySurface.setX(100);
//mySurface.setY(100);
fl = (FrameLayout)act.findViewById(R.id.content);
FrameLayout.LayoutParams params1 = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, Gravity.CENTER);
fl.addView(mySurface);
}

When you have some time could you please install APDE and test the code of the link above? It will only take a minute or two. Because when I use a file in the data folder, I don’t need any permission at all. I’m curious if it can be device-dependent,

@akenaton
I still get no file found using your latest code, both for default data folder and one located with Environment.

I am using Processing 3.5.4

@noel
I have tried the sketch with APDE and it doesn’t work. I have retried previous examples in APDE and they work correctly. I am using Samsung Galaxy A40 with android 10.

Did you use APDE’s App mode? Could you try it with this video, placing it in the data folder? (maybe it’s the file) I’ve tested it on Samsung Android 10.

Edit: Wich sketch are you referring to; mine or yours?

@noel
I have tried your video on your code
and it doesn’t work on my phone.
Where is App mode in APDE?

To the right of the play button you have a phone and <> icon. First = App mode, last = Preview mode.
Did it give any error?