Java Question ExoPlayer Wrapper Development

ronovar

Active Member
Licensed User
Longtime User
I use API23 on comiation so there is no problem..i looked into FFmpeg b4a project library code for implementing onInfoListener and in FFmpeg it works..in ExoPlayer it does not..i will try to investigate why is this only function not working.

Erel...log inside onInfo is showed in B4A but log after
VideoView.setOnInfoListener(new MediaPlayer.OnInfoListener()

is not showed so this code does not execute...any ideas?
 

DonManfred

Expert
Licensed User
Longtime User
upload your source please... i´m sure there must be something wrong
 

ronovar

Active Member
Licensed User
Longtime User
Here is the code:

http://www.filedropper.com/newfolder_41

ExoMediaPlayer.jar is the ExoPlayer from google
ExoMedia.jar is the wrapper that simplicity the ExoPlayer calls.

ExoPlayer.java is the eclipse java source code that creates B4A library.

I im using ExoMedia.jar 2.5.6 because version 3.0 add widget and all wrapper calls is changed.

Just not to forget to include in enclipse android-23.jar B4AShared.jar and Core.jar

And in B4A here is code:

B4X:
'DEFINE - ExoPlayer
    Dim ExoPlayer1 As ExoPlayer

'INITILIZE - ExoPlayer
    ExoPlayer1.Initialize("ExoPlayer1")
   
    'ENABLE - MediaController
    'ExoPlayer1.SetDefaultControlsEnabled(True)
   
    'URL - ExoPlayer
    ExoPlayer1.SetVideoURI("http://myurl/file.mp4")
   
    'PLAY - ExoPlayer
    ExoPlayer1.Start
   
    'ADDVIEW - ExoPlayer
    Activity.AddView(ExoPlayer1, 0, 0, 100%x, 100%y)   

'***********************
'***** E V E N T S *****
'***********************

'OnCompleted - Event
Sub ExoPlayer1_OnCompleted
    Log("ExoPlayer1_OnCompleted")   
End Sub

'OnError - Event
Sub ExoPlayer1_OnError(What As String, Extra As String) As Boolean
    Log("ExoPlayer1_OnError")
    Log(What)
    Log(Extra)
    'B4A - handle event
    Return True
End Sub

'OnInfo - Event
Sub ExoPlayer1_OnInfo(What As String, Extra As String) As Boolean
    Log("ExoPlayer1_OnInfo")
    Log(What)
    Log(Extra)
    'LIBRARY - handle event
    Return False   
End Sub

'OnPrepared - Event
Sub ExoPlayer1_OnPrepared
    Log("ExoPlayer1_OnPrepared")   
End Sub
 

DonManfred

Expert
Licensed User
Longtime User
ExoMediaPlayer.jar is the ExoPlayer from google
ExoMedia.jar is the wrapper that simplicity the ExoPlayer calls.

ExoPlayer.java is the eclipse java source code that creates B4A library.
To write a wrapper in java it need much more than your java.

This is how it would looks like if i would create a wrap for the project ou showed here.

Please note:
I defined all events in the java-code and set the references. But you need to define the events for b4a. This is NOT done here...

I hope it is a bit help for you

B4X:
package de.donmanfred;

import com.devbrackets.android.exomedia.EMVideoView;
import com.devbrackets.android.exomedia.builder.RenderBuilder;
import com.devbrackets.android.exomedia.event.EMMediaProgressEvent;
import com.devbrackets.android.exomedia.listener.EMProgressCallback;
import com.devbrackets.android.exomedia.listener.EMVideoViewControlsCallback;
import com.devbrackets.android.exomedia.listener.ExoPlayerListener;
import com.devbrackets.android.exomedia.util.EMEventBus;
import com.devbrackets.android.exomedia.util.MediaUtil;

import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.media.MediaPlayer;
import android.net.Uri;
import android.support.annotation.DrawableRes;
import android.support.annotation.Nullable;
import android.widget.ImageView;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.BA.Author;
import anywheresoftware.b4a.BA.Hide;
import anywheresoftware.b4a.BA.ShortName;
import anywheresoftware.b4a.BA.Version;
import anywheresoftware.b4a.keywords.Common.DesignerCustomView;
import anywheresoftware.b4a.objects.CustomViewWrapper;
import anywheresoftware.b4a.objects.LabelWrapper;
import anywheresoftware.b4a.objects.PanelWrapper;
import anywheresoftware.b4a.objects.ViewWrapper;
import anywheresoftware.b4a.objects.collections.Map;

@SuppressWarnings("deprecation")
@Version(1.00f)
@ShortName("EMVideoView")
@Author(value = "DonManfred (wrapper)")
//@Permissions(values={"android.permission.INTERNET", "android.permission.ACCESS_NETWORK_STATE"})
//@Events(values={"onSigned(sign As Object)"})
//@DependsOn(values={"android-viewbadger"})
//@DesignerProperties(values = {
//        @Property(key="Checked", displayName="Checked", defaultValue="False", fieldType="Boolean")
//    })
public class EMVideoViewWrapper extends ViewWrapper<EMVideoView> implements DesignerCustomView{
    private BA ba;
    private String eventName;
  private ExoPlayerListener ExoPlayerL = null;
  private MediaPlayer.OnPreparedListener OnPreparedL = null;
  private MediaPlayer.OnCompletionListener OnCompletionL = null;
  private MediaPlayer.OnErrorListener OnErrorL = null;
  private MediaPlayer.OnInfoListener OnInfoL = null;

   
    @Override
    public void DesignerCreateView(PanelWrapper base, LabelWrapper lw, Map props) {
        //getObject().setChecked((Boolean)props.Get("Checked"));
        CustomViewWrapper.replaceBaseWithView(base, getObject());
    }
    public void Initialize(final BA ba, String EventName) {
        _initialize(ba, null, EventName);
    }

    @Hide
    @Override
    public void _initialize(final BA ba, Object activityClass, String EventName) {
        this.eventName = EventName.toLowerCase(BA.cul);
        this.ba = ba;

        final EMVideoView _obj = new EMVideoView(ba.context);
        final String eventName = EventName.toLowerCase(BA.cul);
        setObject(_obj);
      OnInfoL = new MediaPlayer.OnInfoListener(){

            @Override
            public boolean onInfo(MediaPlayer mp, int arg1, int arg2) {
                // TODO Auto-generated method stub
                 if (ba.subExists(eventName + "_oninfo")) {
                  BA.Log("lib:Raising.. "+eventName + "_oninfo()");                               
                  //app.raiseEvent(app.context, eventName+"_pagerendered", i, pageCount, filename+"-" + i + ".png");
                  ba.raiseEventFromDifferentThread(this, null, 0, eventName + "_oninfo", true, new Object[] {mp,arg1,arg2});
              }else {
                  BA.Log("lib: NOTFOUND '"+eventName + "_oninfo");
              }
                return false;
            }
         
      };
      getObject().setOnInfoListener(OnInfoL);

      OnErrorL = new MediaPlayer.OnErrorListener(){

            @Override
            public boolean onError(MediaPlayer mp, int arg1, int arg2) {
                // TODO Auto-generated method stub
                 if (ba.subExists(eventName + "_onerror")) {
                  BA.Log("lib:Raising.. "+eventName + "_onerror()");                               
                  //app.raiseEvent(app.context, eventName+"_pagerendered", i, pageCount, filename+"-" + i + ".png");
                  ba.raiseEventFromDifferentThread(this, null, 0, eventName + "_onerror", true, new Object[] {mp,arg1,arg2});
              }else {
                  BA.Log("lib: NOTFOUND '"+eventName + "_onerror");
              }
                return false;
            }
         
      };
      getObject().setOnErrorListener(OnErrorL);     
       
        OnCompletionL = new MediaPlayer.OnCompletionListener(){

            @Override
            public void onCompletion(MediaPlayer mp) {
                // TODO Auto-generated method stub
                 if (ba.subExists(eventName + "_oncompletion")) {
                  BA.Log("lib:Raising.. "+eventName + "_oncompletion()");                               
                  //app.raiseEvent(app.context, eventName+"_pagerendered", i, pageCount, filename+"-" + i + ".png");
                  ba.raiseEventFromDifferentThread(this, null, 0, eventName + "_oncompletion", true, new Object[] {mp});
              }else {
                  BA.Log("lib: NOTFOUND '"+eventName + "_oncompletion");
              }
               
            }
           
        };
        getObject().setOnCompletionListener(OnCompletionL);

      OnPreparedL = new MediaPlayer.OnPreparedListener(){

            @Override
            public void onPrepared(MediaPlayer mp) {
                // TODO Auto-generated method stub
                 if (ba.subExists(eventName + "_onprepared")) {
                  BA.Log("lib:Raising.. "+eventName + "_onprepared()");                               
                  //app.raiseEvent(app.context, eventName+"_pagerendered", i, pageCount, filename+"-" + i + ".png");
                  ba.raiseEventFromDifferentThread(this, null, 0, eventName + "_onprepared", true, new Object[] {mp});
              }else {
                  BA.Log("lib: NOTFOUND '"+eventName + "_onprepared");
              }
               
            }
      };
      getObject().setOnPreparedListener(OnPreparedL);
       
      EMVideoViewControlsCallback EMVideoViewControlsC = new EMVideoViewControlsCallback(){

            @Override
            public boolean onControlsHidden() {
                // TODO Auto-generated method stub
                 if (ba.subExists(eventName + "_oncontrolshidden")) {
                  BA.Log("lib:Raising.. "+eventName + "_oncontrolshidden()");                               
                  //app.raiseEvent(app.context, eventName+"_pagerendered", i, pageCount, filename+"-" + i + ".png");
                  ba.raiseEventFromDifferentThread(this, null, 0, eventName + "_oncontrolshidden", true, new Object[] {});
              }else {
                  BA.Log("lib: NOTFOUND '"+eventName + "_oncontrolshidden");
              }
                return false;
            }

            @Override
            public boolean onControlsShown() {
                // TODO Auto-generated method stub
                 if (ba.subExists(eventName + "_oncontrolsshown")) {
                  BA.Log("lib:Raising.. "+eventName + "_oncontrolsshown()");                               
                  //app.raiseEvent(app.context, eventName+"_pagerendered", i, pageCount, filename+"-" + i + ".png");
                  ba.raiseEventFromDifferentThread(this, null, 0, eventName + "_oncontrolsshown", true, new Object[] {});
              }else {
                  BA.Log("lib: NOTFOUND '"+eventName + "_oncontrolsshown");
              }
                return false;
            }

            @Override
            public boolean onNextClicked() {
                // TODO Auto-generated method stub
                 if (ba.subExists(eventName + "_onnextclicked")) {
                  BA.Log("lib:Raising.. "+eventName + "_onnextclicked()");                               
                  //app.raiseEvent(app.context, eventName+"_pagerendered", i, pageCount, filename+"-" + i + ".png");
                  ba.raiseEventFromDifferentThread(this, null, 0, eventName + "_onnextclicked", true, new Object[] {});
              }else {
                  BA.Log("lib: NOTFOUND '"+eventName + "_onnextclicked");
              }

                return false;
            }

            @Override
            public boolean onPlayPauseClicked() {
                // TODO Auto-generated method stub
                 if (ba.subExists(eventName + "_onpauseclicked")) {
                  BA.Log("lib:Raising.. "+eventName + "_onpauseclicked()");                               
                  //app.raiseEvent(app.context, eventName+"_pagerendered", i, pageCount, filename+"-" + i + ".png");
                  ba.raiseEventFromDifferentThread(this, null, 0, eventName + "_onpauseclicked", true, new Object[] {});
              }else {
                  BA.Log("lib: NOTFOUND '"+eventName + "_onpauseclicked");
              }
                return false;
            }

            @Override
            public boolean onPreviousClicked() {
                 if (ba.subExists(eventName + "_onprevioisclicked")) {
                  BA.Log("lib:Raising.. "+eventName + "_onprevioisclicked()");                               
                  //app.raiseEvent(app.context, eventName+"_pagerendered", i, pageCount, filename+"-" + i + ".png");
                  ba.raiseEventFromDifferentThread(this, null, 0, eventName + "_onprevioisclicked", true, new Object[] {});
              }else {
                  BA.Log("lib: NOTFOUND '"+eventName + "_onprevioisclicked");
              }
               
                // TODO Auto-generated method stub
                return false;
            }
         
      };
      getObject().setVideoViewControlsCallback(EMVideoViewControlsC);

      EMProgressCallback progressCallback = new EMProgressCallback() {

            @Override
            public boolean onProgressUpdated(EMMediaProgressEvent arg0) {
                // TODO Auto-generated method stub
                 if (ba.subExists(eventName + "_onprogressupdated")) {
                  BA.Log("lib:Raising.. "+eventName + "_onprogressupdated()");                               
                  //app.raiseEvent(app.context, eventName+"_pagerendered", i, pageCount, filename+"-" + i + ".png");
                  ba.raiseEventFromDifferentThread(this, null, 0, eventName + "_onprogressupdated", true, new Object[] {arg0});
              }else {
                  BA.Log("lib: NOTFOUND '"+eventName + "_onprogressupdated");
              }
                return false;
            }
         
      };
      getObject().setProgressCallback(progressCallback);
     

      innerInitialize(ba, eventName, true);
    }
   
       
    public void setReleaseOnDetachFromWindow(boolean releaseOnDetach){
        getObject().setReleaseOnDetachFromWindow(releaseOnDetach);
    }
    public void release(){
        getObject().release();
    }
    public String getUserAgent(){
        return getObject().getUserAgent();
    }
    public void setPreviewImageDrawable(@Nullable Drawable drawable){
        getObject().setPreviewImage(drawable);
    }
    public void setPreviewImageResource(String DrawableName){
      int resourceId=BA.applicationContext.getResources().getIdentifier(DrawableName, "drawable", BA.packageName);
        getObject().setPreviewImage(resourceId);
    }
    public void setPreviewImageBitmap(@Nullable Bitmap bitmap) {
        getObject().setPreviewImage(bitmap);
    }
    public void setPreviewImageUri(@Nullable Uri uri) {
        getObject().setPreviewImage(uri);
  }
    public ImageView getPreviewImageView(){
        return getObject().getPreviewImageView();
    }
    public void setShutterColor(int color){
        getObject().setShutterColor(color);       
    }
    public void setProgressPollDelay(int milliSeconds){
        getObject().setProgressPollDelay(milliSeconds);
    }
    public void setBus(@Nullable EMEventBus bus){
        getObject().setBus(bus);
    }

    public void startProgressPoll(@Nullable EMEventBus bus){
    }
    public void startProgressPoll(EMProgressCallback callback){
    }
    public void startProgressPoll(){
        getObject().startProgressPoll();
    }
  public void stopProgressPoll(){
      getObject().stopProgressPoll();
  }
    public void setDefaultControlsEnabled(boolean enabled){
    }
  public void showDefaultControls(){
  }
  public void setPreviousButtonEnabled(boolean enabled){
  }
  public void setNextButtonEnabled(boolean enabled){
  }
  public void setRewindButtonEnabled(boolean enabled){
  }
  public void setFastForwardButtonEnabled(boolean enabled){
  }
  public void setPlayPauseImages(@DrawableRes int playResourceId, @DrawableRes int pauseResourceId){
  }
  public void setPreviousImageResource(@DrawableRes int resourceId){
  }
  public void setNextImageResource(@DrawableRes int resourceId){
  }
  public void setRewindImageResource(@DrawableRes int resourceId){
  }
  public void setFastForwardImageResource(@DrawableRes int resourceId){
  }
  public void setPreviousButtonRemoved(boolean removed){
      getObject().setPreviousButtonRemoved(removed);
  }
  public void setNextButtonRemoved(boolean removed){
      getObject().setNextButtonRemoved(removed);
  }
  public void setRewindButtonRemoved(boolean removed){
      getObject().setRewindButtonRemoved(removed);
  }
  public void setFastForwardButtonRemoved(boolean removed){
      getObject().setFastForwardButtonRemoved(removed);
  }
  public void setVideoURI(Uri uri){
      getObject().setVideoURI(uri);
  }
  public void setVideoURI2(Uri uri, MediaUtil.MediaType defaultMediaType){
      getObject().setVideoURI(uri, defaultMediaType);
  }
  public void setVideoURI3(Uri uri, RenderBuilder renderBuilder){
      getObject().setVideoURI(uri, renderBuilder);
  }
  public void setVideoPath(String path){
      getObject().setVideoPath(path);
  }
  @Nullable
  public Uri getVideoUri(){
      return getObject().getVideoUri();
  }
  public boolean setVolume(float volume){
      return getObject().setVolume(volume);
  }
  public void reset(){
      getObject().reset();
  }
  public void seekTo(int milliSeconds){
      getObject().seekTo(milliSeconds);
  }
  public boolean isPlaying(){
      return getObject().isPlaying();
  }
  public void start() {
      getObject().start();
  }
  public void pause(){
      getObject().pause();
  }
  public void stopPlayback(){
      getObject().stopPlayback();
  }
  public void suspend(){
      getObject().suspend();
  }
  public long getDuration(){
      return getObject().getDuration();
  }
  public void overrideDuration(int duration){
      getObject().overrideDuration(duration);
  }
  public long getCurrentPosition(){
      return getObject().getCurrentPosition();
  }
  public void setPositionOffset(int offset){
      getObject().setPositionOffset(offset);
  }
  public void restartOverridePosition(){
      getObject().restartOverridePosition();
  }
  public void overridePosition(boolean override){
      getObject().overridePosition(override);
  }
  public int getBufferPercentage(){
      return getObject().getBufferPercentage();
  }
  public void addExoPlayerListener(){
      ExoPlayerL = new  ExoPlayerListener(){

            @Override
            public void onError(Exception e) {
                // TODO Auto-generated method stub
                 if (ba.subExists(eventName + "_onerror")) {
                  BA.Log("lib:Raising.. "+eventName + "_onerror()");                               
                  //app.raiseEvent(app.context, eventName+"_pagerendered", i, pageCount, filename+"-" + i + ".png");
                  ba.raiseEventFromDifferentThread(this, null, 0, eventName + "_onerror", true, new Object[] {e.getMessage()});
              }else {
                  BA.Log("lib: NOTFOUND '"+eventName + "_onerror");
              }

            }

            @Override
            public void onStateChanged(boolean arg0, int arg1) {
                // TODO Auto-generated method stub
                 if (ba.subExists(eventName + "_onstatechange")) {
                  BA.Log("lib:Raising.. "+eventName + "_onstatechange()");                               
                  //app.raiseEvent(app.context, eventName+"_pagerendered", i, pageCount, filename+"-" + i + ".png");
                  ba.raiseEventFromDifferentThread(this, null, 0, eventName + "_onstatechange", true, new Object[] {arg0,arg1});
              }else {
                  BA.Log("lib: NOTFOUND '"+eventName + "_onstatechange");
              }
               
            }

            @Override
            public void onVideoSizeChanged(int arg0, int arg1, int arg2, float arg3) {
                // TODO Auto-generated method stub
                 if (ba.subExists(eventName + "_onvideosizechanged")) {
                  BA.Log("lib:Raising.. "+eventName + "_onvideosizechanged()");                               
                  //app.raiseEvent(app.context, eventName+"_pagerendered", i, pageCount, filename+"-" + i + ".png");
                  ba.raiseEventFromDifferentThread(this, null, 0, eventName + "_onvideosizechanged", true, new Object[] {arg0,arg1,arg2,arg3});
              }else {
                  BA.Log("lib: NOTFOUND '"+eventName + "_onvideosizechanged");
              }
               
            }
      };
      getObject().addExoPlayerListener(ExoPlayerL);
  }
  public void removeExoPlayerListener(){
      getObject().removeExoPlayerListener(ExoPlayerL);
      ExoPlayerL = null;
  }

   
   
}
 

DonManfred

Expert
Licensed User
Longtime User
i forget to wrap a few methods too it seems ;)
Fill them up
B4X:
   public void startProgressPoll(@Nullable EMEventBus bus){
    }
    public void startProgressPoll(EMProgressCallback callback){
    }
B4X:
  public void setDefaultControlsEnabled(boolean enabled){
    }
  public void showDefaultControls(){
  }
  public void setPreviousButtonEnabled(boolean enabled){
  }
  public void setNextButtonEnabled(boolean enabled){
  }
  public void setRewindButtonEnabled(boolean enabled){
  }
  public void setFastForwardButtonEnabled(boolean enabled){
  }
  public void setPlayPauseImages(@DrawableRes int playResourceId, @DrawableRes int pauseResourceId){
  }
  public void setPreviousImageResource(@DrawableRes int resourceId){
  }
  public void setNextImageResource(@DrawableRes int resourceId){
  }
  public void setRewindImageResource(@DrawableRes int resourceId){
  }
  public void setFastForwardImageResource(@DrawableRes int resourceId){
  }
 

ronovar

Active Member
Licensed User
Longtime User
This is excellent..i im just beginner on creating library...could you write whole b4a library and post here on forum?

You finished excellent work and this is more that basic functions that users here need....just to ask if you could write function to change quality name of stream..my stream video quality is default low i need to set it high.

function that need to be called is:

setVideoUri(videoUri, getRenderBuilder(MediaSourceType.get(uri), uri));
 

DonManfred

Expert
Licensed User
Longtime User
i saw the reference to the RenderBuilder while write the code above... I guess the builder needs another wrap (another object inside the wrapper)...

could you write whole b4a library and post here on forum?
As i already did a lot of it actually in the code above it could be a good idea. ;)
I´ll have a look at it this week (end)
 

ronovar

Active Member
Licensed User
Longtime User
great please add this so that user could set quality and if you could add setsubtitle from mkv...this library have mkv subitle parser...so it will be very nice to include in B4A.

I think that it will be the best video player for android and your porting to B4A will make many people here on forum happy :)
 

ronovar

Active Member
Licensed User
Longtime User
And i forget..this is 2.5.6 release....new release 3.0.0-preview1 adds a lot new feautures...so it will be good point if you have time and port this library with 3.0.0 version to B4A.

Version 3.0.0 adds widgets and more nice onevents handlers...so this will be extra video player library.

Here is url for 3.0.0-preview1

https://bintray.com/brianwernick/maven/ExoMedia/view#files/com/devbrackets/android/exomedia

https://github.com/brianwernick/ExoMedia

And i im begginer in android java library create..so you are expert and i know that when you have time that library will be extra ported.


just please if you can add basic functions that users need:

- Stop
- Start
- OnBufferPerctange
- DisableMKVSubitleFromFile
- SetMKVSubtitleFromFile
- OnErrorListener
- Show MediaController
- Hide MediaController
- setVideoURI
- setVideoQuality (LOW or MIDDLE or HIGH)
 
Top