Android Question How to disable subtitles in VideoView?

ronovar

Active Member
Licensed User
Longtime User
I im using VideoView (Native Android OS player), and i play mkv file witch have encoded subtitle in it. Subtitles is show on screen automatically but is not syncronizes..so i have a few monts ago write my own srt parser for subtitle..and would like to know id is possible to disable showing subitles from mkv file?

JavaObject or Reflector method if is possible? I have search android api but i did not find nothing about disabling integrated mkv subitles.

Does exists this api function?
 

ronovar

Active Member
Licensed User
Longtime User
Thanks for link...one idea is if is possible to disable this function:

B4X:
public void addSubtitleSource (InputStream is, MediaFormat format)

Or just setting fake MediaFormat when video is start playing..this way this function will return according to api MEDIA_INFO_SUBTITLE_TIMED_OUT so in theory subtitles won't show.

How to set fake MediaFormat in this api so that i can try?
 
Upvote 0

ronovar

Active Member
Licensed User
Longtime User
I found api:

I just need to calldeselectTrack(int ID of subtitle track displayed) and subtitles will be gone:

http://developer.android.com/reference/android/media/MediaPlayer.html#deselectTrack(int)
http://developer.android.com/reference/android/media/MediaPlayer.html#getTrackInfo()

so i need first call

-
public TrackInfo[] getTrackInfo ()

to get all track id

and then call

-
public void deselectTrack (int index)

to deselect subtitle track


How can i run this in b4a using java object or reflector?
 
Upvote 0

somed3v3loper

Well-Known Member
Licensed User
Longtime User
I think we need some help from Erel to provide Audio MediaPlayer class name so we try to access it through JavaObject
 
Upvote 0

ronovar

Active Member
Licensed User
Longtime User
Yes you are right...Erel could help.....i think we have:

OBJECT: android.media.MediaPlayer.TrackInfo
EVENT: getTrackInfo()

So my try is this:

B4X:
Dim jo As JavaObject= "android.media.MediaPlayer.TrackInfo"

Log(jo.RunMethodJO("getTrackInfo", Null))

I im getting this error:


java.lang.RuntimeException: Method: getTrackInfo not found in: java.lang.String

So the problem is that i call wrong or it does not event exists in this event
 
Upvote 0

somed3v3loper

Well-Known Member
Licensed User
Longtime User
This is just the start , I will add to this later but just in case you or any other one want to complete it
I am not sure this is the best solution ( I set android.media.MediaPlayer.OnInfoListener to get an instance of MediaPlayer )

B4X:
package smm.videoview;

import java.io.File;

import android.content.res.AssetFileDescriptor;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnInfoListener;
import android.net.Uri;
import android.widget.MediaController;
import android.widget.SeekBar;
import android.widget.VideoView;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.BA.ActivityObject;
import anywheresoftware.b4a.BA.DontInheritEvents;
import anywheresoftware.b4a.BA.Events;
import anywheresoftware.b4a.BA.Hide;
import anywheresoftware.b4a.BA.ShortName;
import anywheresoftware.b4a.objects.MediaPlayerWrapper;
import anywheresoftware.b4a.objects.ViewWrapper;
import android.media.MediaPlayer.TrackInfo;
import java.util.List;
import java.util.Arrays;
import java.util.ArrayList;

/**
* VideoView is a view that allows you to play video media inside your application.
*The VideoView optionally shows a media controller when the user touches the view.
*The Completed event is raised when playback is completed.
*Simple example of using VideoView:<code>
    *Sub Globals
    *   Dim vv As VideoView
    *End Sub
    *Sub Activity_Create(FirstTime As Boolean)
    *   vv.Initialize("vv")
    *   Activity.AddView(vv, 10dip, 10dip, 250dip, 250dip)
    *   vv.LoadVideo(File.DirRootExternal, "somefile.mp4")
    *   vv.Play
    *End Sub
    *Sub vv_Complete
    *   Log("Playing completed")
    *End Sub</code>
*
*/
@DontInheritEvents
@ShortName("VideoViewEx")
@ActivityObject
@Events(values={"Complete"})
public class VideoWrapperEx extends ViewWrapper<VideoView>{
   /**
    * Initialize the objects and sets the name of the subs that will handle the events.
    */
   @Override
   public void Initialize(final BA ba, String EventName) {
      super.Initialize(ba, EventName);
   }
   @Override
   @Hide
   public void innerInitialize(final BA ba, final String eventName, boolean keepOldObject) {
      if (!keepOldObject)
         setObject(new VideoView(ba.context));
      super.innerInitialize(ba, eventName, true);
      setMediaControllerEnabled(true);
      getObject().setOnCompletionListener(new MediaPlayer.OnCompletionListener() {

         @Override
         public void onCompletion(MediaPlayer mp) {
            ba.raiseEvent(VideoWrapperEx.this, eventName + "_complete");
         }
        
      });
     
        getObject().setOnInfoListener(new MediaPlayer.OnInfoListener() {
          @Override
          public boolean onInfo(MediaPlayer smp, int what, int extra) {
           
               TrackInfo[] trackInfos = smp.getTrackInfo();
            
              for(int i =0; i < trackInfos.length ; i++) {
                  TrackInfo ti =trackInfos[i];
                  ba.Log("Track: "+ti);
                 
              }
                 
           
              ba.raiseEvent2(this,true, eventName + "_info",false,  new Object[0]);
          
          return true;
          }
        });
     
   }
   /**
    * Loads a video file and prepares it for playing.
    *It is not possible to load files from the assets folder.
    *Advanced: you can pass "http" to the Dir parameter and then a full URL (including http) to the FileName.
    *In this case the online video will be streamed. Note that you need to add the INTERNET permission for this to work.
    */
   public void LoadVideo(String Dir, String FileName) {
      if (Dir.equals(anywheresoftware.b4a.objects.streams.File.getDirAssets())) {
         throw new RuntimeException("Cannot load video from assets folder.");
      }
      else if (Dir.equals(anywheresoftware.b4a.objects.streams.File.ContentDir)) {
         getObject().setVideoPath(FileName);
      }
      else if (Dir.equals("http")) {
         getObject().setVideoPath(FileName);
      }
      else {
         getObject().setVideoPath(new File(Dir, FileName).toString());
      }
   }
   /**
    * Starts or resumes playing.
    */
   public void Play() {
      getObject().start();
   }
   /**
    * Pauses the playback.
    */
   public void Pause() {
      getObject().pause();
   }
   /**
    * Stops the playback.
    */
   public void Stop() {
      getObject().stopPlayback();
   }
   /**
    * Tests whether the video is currently playing.
    */
   public boolean IsPlaying() {
      return getObject().isPlaying();
   }
   /**
    * Gets or sets the playing position (in milliseconds).
    */
   public int getPosition() {
      return getObject().getCurrentPosition();
   }
   public void setPosition(int v) {
      getObject().seekTo(v);
   }
   /**
    * Gets the video duration in milliseconds.
    */
   public int getDuration() {
      return getObject().getDuration();
   }
      
   /**
    * Sets whether the media controller is enabled. It is enabled by default.
    *Note that the media player gets attached to the VideoView parent.
    */
   public void setMediaControllerEnabled (boolean v) {
      getObject().setMediaController(v ? new MediaController(ba.context) : null);
   }
   @Override
   public String toString() {
      if (getObjectOrNull() == null)
         return super.toString();
      if (getObject().isPlaying() == false)
         return "Not playing";
      else
         return "Playing, Position=" + getPosition() + ", Duration=" + getDuration();
   }
}
 
Upvote 0

ronovar

Active Member
Licensed User
Longtime User
Thanks as your excellent idea to get MediaPlayer instance i write this in B4A and it works:

B4X:
Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.
    Dim vv As VideoView
End Sub

Sub Activity_Create(FirstTime As Boolean)   
    vv.Initialize("vv")
    Activity.AddView(vv, 0, 0, 100%x, 100%y)
   
    AddInfoHandler(vv,"VVInfo")
    vv.LoadVideo("http", "http://url/movie.mkv")
    vv.Play
End Sub

Sub AddInfoHandler(v As VideoView, EventName As String)
   Dim jo As JavaObject = v
   Dim e As Object = jo.CreateEventFromUI("android.media.MediaPlayer.OnInfoListener", EventName, True)
   jo.RunMethod("setOnInfoListener", Array(e))
End Sub

Sub VVInfo_Event (MethodName As String,Args() As Object) As Object
    Dim r As Reflector
    r.Target = Args(0)
    Dim TrackInfo() As Int
    TrackInfo = r.RunMethod("getTrackInfo")
   
    For i=0 To TrackInfo.Length-1
        Log(CStr(TrackInfo(i)))
    Next
  Return Null
End Sub

Sub CStr(o As Object) As String
 Return "" & o
End Sub

I get in log:

B4X:
android.media.MediaPlayer$TrackInfo@4192e010
android.media.MediaPlayer$TrackInfo@4192e0f8
android.media.MediaPlayer$TrackInfo@4192e1e0

I was expecting TrackID Index (0, 1, 2)

first is i think video(4192e010), second is audio(4192e0f8), and third is subtitle(4192e1e0)
 
Upvote 0

ronovar

Active Member
Licensed User
Longtime User
Ok now that i have MediaPlayer Object that i can send parameters do you have idea or example how to call public void deselectTrack (int index)?
 
Upvote 0

somed3v3loper

Well-Known Member
Licensed User
Longtime User
Now I think we have to do something similar to this

B4X:
For i=0 To TrackInfo.Length-1
        dim tr as javaobject =CStr(TrackInfo(i))
      dim trtype as int = tr.RunMethod("getTrackType",null)
      if trtype = 4 then
           'this track is subtitle
           dim idoftrack as int = mp.RunMethod("getSelectedTrack",null)
          'We still need to figure out wich arg is mp :D
           mp.RunMethod("deselectTrack",array as int (int index))
     end if
    Next
 
Upvote 0

ronovar

Active Member
Licensed User
Longtime User
Thanks for code above..do you have idea how we can MP (MediaPlayer) handler? Isn't MP Args(0) ?

I now write this code that you post above and if works:

B4X:
    'DEFINE - MediaPlayer
    Dim MP As Object = Args(0)

    'DEFINE - Reflector
    Dim r As Reflector
  
    'SET - Target
    r.Target = MP
  
    'DEFINE - TrackInfo() Array
    Dim TrackInfo() As Object
  
    'GET - TrackInfo To TrackInfo() Array
    TrackInfo = r.RunMethod("getTrackInfo")

    'LOOP - Throught Tracks
    For i=0 To TrackInfo.Length-1
        'SET - TrackInfo
        Dim Track As JavaObject = TrackInfo(i)

        'GET - Track Type (Video, Audio, Subtitle)
        Dim TrackType As Int = Track.RunMethod("getTrackType", Null)
      
        'CHECK - Subtitle
        If (TrackType = 3) Then
            Log("SubtitleTrack..."&TrackType)
        End If
    Next

In logCat i see:

SubtitleTrack...3
When i add to code:

B4X:
Log(r.TypeName)

I get:

android.media.MediaPlayer

So Args(0) is the MediaPlayer object....why can i pass to this object

getSelectedTrack
deselectTrack
 
Last edited:
Upvote 0

somed3v3loper

Well-Known Member
Licensed User
Longtime User
Check this :

B4X:
Sub VVInfo_Event (MethodName As String,Args() As Object) As Object
   
    Dim mp As JavaObject = Args(0)
    Dim jTrackInfo As JavaObject
    jTrackInfo.InitializeArray("android.media.MediaPlayer.TrackInfo",mp.RunMethod("getTrackInfo",Null))

    Dim jcontext As JavaObject
    jcontext.InitializeContext
    jcontext.RunMethod("deselectsubtitle",Array(mp,jTrackInfo))

  Return Null
End Sub


B4X:
#If Java
import android.media.MediaPlayer;
import android.media.MediaPlayer.TrackInfo;
import android.util.Log;
public void deselectsubtitle (MediaPlayer mps, TrackInfo[] trackInfos) {

//TrackInfo[] trackInfos = mps.getTrackInfo();
            for (int i = 0; i < trackInfos.length; i++) {
                TrackInfo trackInfo = trackInfos[i];
               
               
                if (trackInfo.getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) {
                    mps.deselectTrack(i);
                   
                    Log.e("B4A","Deselected track: "+i);
               
                }
                else
                {
                Log.e("B4A","Subtitle not found");
                }
               
            }

}
#End if
 
Upvote 0

ronovar

Active Member
Licensed User
Longtime User
Thanks for code above compile fine..when run apk i get this in my log:

B4X:
java.lang.RuntimeException: Method: deselectsubtitle not found in: b4a.example.main
 
Upvote 0

ronovar

Active Member
Licensed User
Longtime User
Yes...you are right i forget to include java code into b4a...now i get this error:

B4X:
src\b4a\example\main.java:575: error: cannot find symbol
                if (trackInfo.getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) {
                                                         ^
  symbol:   variable MEDIA_TRACK_TYPE_SUBTITLE
  location: class TrackInfo
1 error

So i see this:

http://developer.android.com/refere...ayer.TrackInfo.html#MEDIA_TRACK_TYPE_SUBTITLE

It needs to declare variable to give it value 0x00000004

How to declare it and append value? i try this but would not compile:

public static final int MEDIA_TRACK_TYPE_SUBTITLE = 0x00000004
 
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
I guess it should work using the int directly...
B4X:
if (trackInfo.getTrackType() == 4) {
 
Upvote 0

somed3v3loper

Well-Known Member
Licensed User
Longtime User
It works without problems here
Do you have
import android.media.MediaPlayer.TrackInfo; ?

I do not have subtitles but that shouldn't make any difference in such case .
 
Upvote 0

ronovar

Active Member
Licensed User
Longtime User
I have added your code:

B4X:
#If Java
import android.media.MediaPlayer;
import android.media.MediaPlayer.TrackInfo;
import android.util.Log;
public void deselectsubtitle (MediaPlayer mps, TrackInfo[] trackInfos) {

//TrackInfo[] trackInfos = mps.getTrackInfo();

            for (int i = 0; i < trackInfos.length; i++) {
                TrackInfo trackInfo = trackInfos[i];
           Log.e("B4A","trackInfo.getTrackType: "+trackInfo.getTrackType());    
               
                if (trackInfo.getTrackType() == 3) {
                    mps.deselectTrack(i);
                   
                    Log.e("B4A","Deselected track: "+i);
               
                }
                else
                {
                Log.e("B4A","Subtitle not found");
                }
               
            }

}
#End if

And in B4A Log i see this:

** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
trackInfo.getTrackType: 1
Subtitle not found
trackInfo.getTrackType: 2
Subtitle not found
trackInfo.getTrackType: 3
Subtitle not found


So you see that script does not detect subtitles in mkv video....i see subtitles on screen witch is in mkv...any ideas what i have done wrong?

I see that:

public static final int MEDIA_TRACK_TYPE_SUBTITLE
Constant Value: 4 (0x00000004)

It detect track 3, apk run and subtitles is not deselected...then i change 3 to 4 witch is:

public static final int MEDIA_TRACK_TYPE_TIMEDTEXT
Constant Value: 3 (0x00000003)

And APK compiles and crash when loading video..in log i see this:

** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
trackInfo.getTrackType: 1
Subtitle not found
trackInfo.getTrackType: 2
Subtitle not found
trackInfo.getTrackType: 3
Error occurred on line: 66 (Main)
java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at anywheresoftware.b4j.object.JavaObject.RunMethod(JavaObject.java:131)
at b4a.example.main._vvprepared_event(main.java:541)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:702)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:336)
at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:246)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:134)
at anywheresoftware.b4a.BA$1.run(BA.java:293)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:788)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:604)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.RuntimeException: failure code: -32
at android.media.MediaPlayer.invoke(MediaPlayer.java:710)
at android.media.MediaPlayer.selectOrDeselectInbandTrack(MediaPlayer.java:3088)
at android.media.MediaPlayer.selectOrDeselectTrack(MediaPlayer.java:3077)
at android.media.MediaPlayer.deselectTrack(MediaPlayer.java:3053)
at b4a.example.main.deselectsubtitle(main.java:558)
... 22 more

So i think there is two methods that we don't need to mix...first is:

MEDIA_TRACK_TYPE_SUBTITLE (I think external subtitle loaded with SRT extension for example, this is ID: 3, this i don't need because i im using my own SRT subtitle parser)
And
MEDIA_TRACK_TYPE_TIMEDTEXT (I think this is encoded into mkv file subitle, this i need to disable this is ID: 4)

So how to set apk to disable timedtext?
 
Upvote 0

somed3v3loper

Well-Known Member
Licensed User
Longtime User
Did you try it DonManfred's way ?
Every MEDIA_TRACK_TYPE_XXXXX type represents an int value which you can get from android developer website
 
Upvote 0
Top