[Android] [PATCH] Dynamically respond to video ES changes

Edward Wang edward.c.wang at compdigitec.com
Tue Jul 3 02:15:24 CEST 2012


This will allow us to support dynamically ES-changing streams, more streaming formats (TS) and more video formats (such as OGG video).
---
 Version 2, fixes small problem with local video files

 vlc-android/jni/libvlcjni.c                        |   12 ++++-
 vlc-android/src/org/videolan/vlc/AudioService.java |   51 +++++++++++++++++++-
 .../org/videolan/vlc/AudioServiceController.java   |   12 +++++
 vlc-android/src/org/videolan/vlc/EventManager.java |    4 +-
 vlc-android/src/org/videolan/vlc/LibVLC.java       |    4 ++
 .../src/org/videolan/vlc/gui/MainActivity.java     |   28 ++++-------
 .../vlc/gui/video/VideoPlayerActivity.java         |   51 +++++++++++++++++++-
 .../org/videolan/vlc/interfaces/IAudioService.aidl |    1 +
 8 files changed, 137 insertions(+), 26 deletions(-)

diff --git a/vlc-android/jni/libvlcjni.c b/vlc-android/jni/libvlcjni.c
index 1d07a82..18867ff 100644
--- a/vlc-android/jni/libvlcjni.c
+++ b/vlc-android/jni/libvlcjni.c
@@ -164,6 +164,11 @@ static void vlc_event_callback(const libvlc_event_t *ev, void *data)
     JNIEnv *env;
     JavaVM *myVm = data;
 
+    int ev_opt_data = 0;
+    if(ev->type == libvlc_MediaPlayerVout) {
+        /* For determining the vout/ES track change */
+        ev_opt_data = ev->u.media_player_vout.new_count;
+    }
     bool isAttached = false;
 
     if (eventManagerInstance == NULL)
@@ -187,9 +192,9 @@ static void vlc_event_callback(const libvlc_event_t *ev, void *data)
     }
 
     /* Find the callback ID */
-    jmethodID methodID = (*env)->GetMethodID(env, cls, "callback", "(I)V");
+    jmethodID methodID = (*env)->GetMethodID(env, cls, "callback", "(II)V");
     if (methodID) {
-        (*env)->CallVoidMethod(env, eventManagerInstance, methodID, ev->type);
+        (*env)->CallVoidMethod(env, eventManagerInstance, methodID, ev->type, ev_opt_data);
     } else {
         LOGE("EventManager: failed to get the callback method");
     }
@@ -365,7 +370,7 @@ void Java_org_videolan_vlc_LibVLC_setEventManager(JNIEnv *env, jobject thiz, job
         return;
     }
 
-    jmethodID methodID = (*env)->GetMethodID(env, cls, "callback", "(I)V");
+    jmethodID methodID = (*env)->GetMethodID(env, cls, "callback", "(II)V");
     if (!methodID) {
         LOGE("setEventManager: failed to get the callback method");
         return;
@@ -526,6 +531,7 @@ void Java_org_videolan_vlc_LibVLC_readMedia(JNIEnv *env, jobject thiz,
         libvlc_MediaPlayerPaused,
         libvlc_MediaPlayerEndReached,
         libvlc_MediaPlayerStopped,
+        libvlc_MediaPlayerVout,
     };
     int i;
     for (i = 0; i < (sizeof(mp_events) / sizeof(*mp_events)); ++i)
diff --git a/vlc-android/src/org/videolan/vlc/AudioService.java b/vlc-android/src/org/videolan/vlc/AudioService.java
index a1f4226..b2fddef 100644
--- a/vlc-android/src/org/videolan/vlc/AudioService.java
+++ b/vlc-android/src/org/videolan/vlc/AudioService.java
@@ -30,6 +30,7 @@ import java.util.Stack;
 
 import org.videolan.vlc.gui.MainActivity;
 import org.videolan.vlc.gui.audio.AudioPlayerActivity;
+import org.videolan.vlc.gui.video.VideoPlayerActivity;
 import org.videolan.vlc.interfaces.IAudioService;
 import org.videolan.vlc.interfaces.IAudioServiceCallback;
 import org.videolan.vlc.widget.VLCAppWidgetProvider;
@@ -277,6 +278,21 @@ public class AudioService extends Service {
                     executeUpdate();
                     next();
                     break;
+                case EventManager.MediaPlayerVout:
+                    if(msg.getData().getInt("data") > 0) {
+                        Log.i(TAG, "Obtained video track");
+                        mMediaList.clear();
+                        hideNotification();
+
+                        // Got video, switch to the video player
+                        Intent intent = new Intent(VLCApplication.getAppContext(), VideoPlayerActivity.class);
+                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+                        intent.putExtra("itemLocation", mCurrentMedia.getLocation());
+                        // Don't lose the currently playing stream
+                        intent.putExtra("dontParse", true);
+                        startActivity(intent);
+                    }
+                    break;
                 default:
                     Log.e(TAG, "Event not handled");
                     break;
@@ -611,8 +627,41 @@ public class AudioService extends Service {
             }
 
             if (mCurrentMedia != null)
-                mLibVLC.readMedia(mCurrentMedia.getLocation(), true);
+                mLibVLC.readMedia(mCurrentMedia.getLocation());
+            showNotification();
+        }
+
+        @Override
+        public void showWithoutParse(String URI) throws RemoteException {
+            Log.v(TAG, "Showing playing URI " + URI);
+            // Show an URI without interrupting/losing the current stream
+
+            if(!mLibVLC.isPlaying())
+                return;
+            mEventManager.addHandler(mEventHandler);
+            mMediaList.clear();
+            mPrevious.clear();
+            // Prevent re-parsing the media, which would mean losing the connection
+            mCurrentMedia = new Media(
+                    getApplicationContext(),
+                    URI,
+                    0,
+                    0,
+                    Media.TYPE_AUDIO,
+                    null,
+                    URI,
+                    VLCApplication.getAppContext().getString(R.string.unknown_artist),
+                    VLCApplication.getAppContext().getString(R.string.unknown_genre),
+                    VLCApplication.getAppContext().getString(R.string.unknown_album),
+                    0,
+                    0,
+                    "");
+            mMediaList.add(mCurrentMedia);
+
+            // Notify everyone
+            mHandler.sendEmptyMessage(SHOW_PROGRESS);
             showNotification();
+            executeUpdate();
         }
 
         @Override
diff --git a/vlc-android/src/org/videolan/vlc/AudioServiceController.java b/vlc-android/src/org/videolan/vlc/AudioServiceController.java
index 5f6ef61..98627e4 100644
--- a/vlc-android/src/org/videolan/vlc/AudioServiceController.java
+++ b/vlc-android/src/org/videolan/vlc/AudioServiceController.java
@@ -212,6 +212,18 @@ public class AudioServiceController implements IAudioPlayerControl {
         updateAudioPlayer();
     }
 
+    public void showWithoutParse(String u) {
+        if (mAudioServiceBinder == null) {
+            return;
+        }
+        try {
+            mAudioServiceBinder.showWithoutParse(u);
+        } catch (RemoteException e) {
+            Log.e(TAG, "remote procedure call failed: showWithoutParse()");
+        }
+        updateAudioPlayer();
+    }
+
     @Override
     public String getAlbum() {
         String album = null;
diff --git a/vlc-android/src/org/videolan/vlc/EventManager.java b/vlc-android/src/org/videolan/vlc/EventManager.java
index 6dcb44c..19e88a1 100644
--- a/vlc-android/src/org/videolan/vlc/EventManager.java
+++ b/vlc-android/src/org/videolan/vlc/EventManager.java
@@ -57,6 +57,7 @@ public class EventManager {
     //public static final int MediaPlayerTitleChanged         = 0x10f;
     //public static final int MediaPlayerSnapshotTaken        = 0x110;
     //public static final int MediaPlayerLengthChanged        = 0x111;
+    public static final int MediaPlayerVout                   = 0x112;
 
     //public static final int MediaListItemAdded              = 0x200;
     //public static final int MediaListWillAddItem            = 0x201;
@@ -111,9 +112,10 @@ public class EventManager {
     }
 
     /** This method is called by a native thread **/
-    public void callback(int event) {
+    public void callback(int event, int optional_data) {
         Bundle b = new Bundle();
         b.putInt("event", event);
+        b.putInt("data", optional_data);
         for (int i = 0; i < mEventHandler.size(); i++) {
             Message msg = Message.obtain();
             msg.setData(b);
diff --git a/vlc-android/src/org/videolan/vlc/LibVLC.java b/vlc-android/src/org/videolan/vlc/LibVLC.java
index b36791d..b7852bc 100644
--- a/vlc-android/src/org/videolan/vlc/LibVLC.java
+++ b/vlc-android/src/org/videolan/vlc/LibVLC.java
@@ -227,6 +227,10 @@ public class LibVLC {
         mAout.release();
     }
 
+    public void readMedia(String mrl) {
+        readMedia(mLibVlcInstance, mrl, false);
+    }
+
     /**
      * Read a media.
      */
diff --git a/vlc-android/src/org/videolan/vlc/gui/MainActivity.java b/vlc-android/src/org/videolan/vlc/gui/MainActivity.java
index 8d21e80..d2efe75 100644
--- a/vlc-android/src/org/videolan/vlc/gui/MainActivity.java
+++ b/vlc-android/src/org/videolan/vlc/gui/MainActivity.java
@@ -20,7 +20,6 @@
 
 package org.videolan.vlc.gui;
 
-import java.io.IOException;
 import java.util.ArrayList;
 
 import org.videolan.vlc.AudioService;
@@ -34,7 +33,6 @@ import org.videolan.vlc.VLCCallbackTask;
 import org.videolan.vlc.gui.audio.AudioBrowserFragment;
 import org.videolan.vlc.gui.video.VideoListAdapter;
 import org.videolan.vlc.gui.video.VideoListFragment;
-import org.videolan.vlc.gui.video.VideoPlayerActivity;
 import org.videolan.vlc.interfaces.ISortable;
 import org.videolan.vlc.widget.AudioMiniPlayer;
 
@@ -479,23 +477,15 @@ public class MainActivity extends SherlockFragmentActivity {
                             AudioServiceController c = AudioServiceController.getInstance();
                             String s = input.getText().toString();
 
-                            try {
-                                if(!LibVLC.getExistingInstance().hasVideoTrack(s)) {
-                                    Log.d(TAG, "Auto-detected audio for " + s);
-                                    ArrayList<String> media = new ArrayList<String>();
-                                    media.add(input.getText().toString());
-                                    c.append(media);
-                                } else {
-                                    Log.d(TAG, "Auto-detected Video for " + s);
-                                    Intent intent = new Intent(getApplicationContext(),
-                                                               VideoPlayerActivity.class);
-                                    intent.putExtra("itemLocation", s);
-                                    startActivity(intent);
-                                }
-                            } catch(IOException e) {
-                                /* VLC is unable to open the MRL */
-                                return;
-                            }
+                            /* Use the audio player by default. If a video track is
+                             * detected, then it will automatically switch to the video
+                             * player. This allows us to support more types of streams
+                             * (for example, RTSP and TS streaming) where ES can be
+                             * dynamically adapted rather than a simple scan.
+                             */
+                            ArrayList<String> media = new ArrayList<String>();
+                            media.add(s);
+                            c.append(media);
                         }
 
                         @Override
diff --git a/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.java b/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.java
index 3612444..6870768 100644
--- a/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.java
+++ b/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.java
@@ -25,12 +25,14 @@ import java.io.UnsupportedEncodingException;
 import java.lang.reflect.Method;
 import java.net.URLDecoder;
 
+import org.videolan.vlc.AudioServiceController;
 import org.videolan.vlc.EventManager;
 import org.videolan.vlc.LibVLC;
 import org.videolan.vlc.LibVlcException;
 import org.videolan.vlc.R;
 import org.videolan.vlc.Util;
 import org.videolan.vlc.gui.PreferencesActivity;
+import org.videolan.vlc.gui.audio.AudioPlayerActivity;
 import org.videolan.vlc.interfaces.IPlayerControl;
 import org.videolan.vlc.interfaces.OnPlayerControlListener;
 import org.videolan.vlc.widget.PlayerControlClassic;
@@ -114,6 +116,14 @@ public class VideoPlayerActivity extends Activity {
     private ImageButton mLock;
     private ImageButton mSize;
 
+    // what video?
+    private String mLocation;
+    /**
+     * For uninterrupted switching between audio and video mode
+     */
+    private boolean mSwitchingView;
+    private boolean mEndReached;
+
     // size of the video
     private int mVideoHeight;
     private int mVideoWidth;
@@ -178,6 +188,9 @@ public class VideoPlayerActivity extends Activity {
         mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
         mAudioMax = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
 
+        mSwitchingView = false;
+        mEndReached = false;
+
         registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
 
         try {
@@ -200,10 +213,15 @@ public class VideoPlayerActivity extends Activity {
     protected void onStart() {
         super.onStart();
         dimStatusBar(true);
+        mSwitchingView = false;
     }
 
     @Override
     protected void onPause() {
+        if(mSwitchingView) {
+            super.onPause();
+            return;
+        }
         long time = 0;
         if (mLibVLC.isPlaying()) {
             time = mLibVLC.getTime() - 5000;
@@ -222,7 +240,7 @@ public class VideoPlayerActivity extends Activity {
     @Override
     protected void onDestroy() {
         unregisterReceiver(mBatteryReceiver);
-        if (mLibVLC != null) {
+        if (mLibVLC != null && !mSwitchingView) {
             mLibVLC.stop();
         }
 
@@ -231,9 +249,23 @@ public class VideoPlayerActivity extends Activity {
 
         mAudioManager = null;
 
+        if(mSwitchingView) {
+            Log.d(TAG, "mLocation = \"" + mLocation + "\"");
+            AudioServiceController.getInstance().showWithoutParse(mLocation);
+            Intent i = new Intent(this, AudioPlayerActivity.class);
+            i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+            startActivity(i);
+        }
+        //AudioServiceController.getInstance().unbindAudioService(this);
         super.onDestroy();
     }
 
+    @Override
+    protected void onResume() {
+        AudioServiceController.getInstance().bindAudioService(this);
+        super.onResume();
+    }
+
     private final BroadcastReceiver mBatteryReceiver = new BroadcastReceiver()
     {
         @Override
@@ -370,8 +402,17 @@ public class VideoPlayerActivity extends Activity {
                 case EventManager.MediaPlayerEndReached:
                     Log.i(TAG, "MediaPlayerEndReached");
                     /* Exit player when reach the end */
+                    mEndReached = true;
                     VideoPlayerActivity.this.finish();
                     break;
+                case EventManager.MediaPlayerVout:
+                    if(msg.getData().getInt("data") == 0 && !mEndReached) {
+                        /* Video track lost, open in audio mode */
+                        Log.i(TAG, "Video track lost, switching to audio");
+                        VideoPlayerActivity.this.mSwitchingView = true;
+                        VideoPlayerActivity.this.finish();
+                    }
+                    break;
                 default:
                     Log.e(TAG, "Event not handled");
                     break;
@@ -831,6 +872,7 @@ public class VideoPlayerActivity extends Activity {
         String title = null;
         String lastLocation = null;
         long lastTime = 0;
+        boolean dontParse = false;
         SharedPreferences preferences = getSharedPreferences(PreferencesActivity.NAME, MODE_PRIVATE);
 
         if (getIntent().getAction() != null
@@ -840,12 +882,17 @@ public class VideoPlayerActivity extends Activity {
         } else {
             /* Started from VideoListActivity */
             location = getIntent().getExtras().getString("itemLocation");
+            dontParse = getIntent().getExtras().getBoolean("dontParse");
         }
 
-        if (location != null && location.length() > 0) {
+        if(dontParse)
+            mLocation = location;
+        if (location != null && location.length() > 0 && !dontParse) {
             mLibVLC.readMedia(location, false);
             mSurface.setKeepScreenOn(true);
 
+            mLocation = location;
+
             // Save media for next time, and restore position if it's the same one as before
             lastLocation = preferences.getString(PreferencesActivity.LAST_MEDIA, null);
             lastTime = preferences.getLong(PreferencesActivity.LAST_TIME, 0);
diff --git a/vlc-android/src/org/videolan/vlc/interfaces/IAudioService.aidl b/vlc-android/src/org/videolan/vlc/interfaces/IAudioService.aidl
index 4525d94..600dc42 100644
--- a/vlc-android/src/org/videolan/vlc/interfaces/IAudioService.aidl
+++ b/vlc-android/src/org/videolan/vlc/interfaces/IAudioService.aidl
@@ -50,4 +50,5 @@ interface IAudioService {
     void addAudioCallback(IAudioServiceCallback cb);
     void removeAudioCallback(IAudioServiceCallback cb);
     void detectHeadset(boolean enable);
+    void showWithoutParse(String URI);
 }
-- 
1.7.5.4



More information about the Android mailing list