[Android] [PATCH] Dynamically respond to video ES changes
Edward Wang
edward.c.wang at compdigitec.com
Wed Jul 4 20:55:08 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 4, rebased
vlc-android/jni/libvlcjni.c | 12 ++++-
vlc-android/src/org/videolan/vlc/AudioService.java | 51 +++++++++++++++++++-
.../org/videolan/vlc/AudioServiceController.java | 7 +++
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 | 45 ++++++++++++++++-
.../org/videolan/vlc/interfaces/IAudioService.aidl | 1 +
8 files changed, 126 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 cefaf23..389e2ff 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;
@@ -613,13 +629,46 @@ public class AudioService extends Service {
}
if (mCurrentMedia != null) {
- mLibVLC.readMedia(mCurrentMedia.getLocation(), true);
+ mLibVLC.readMedia(mCurrentMedia.getLocation());
showNotification();
updateWidget(AudioService.this);
}
}
@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
public void append(List<String> mediaPathList) throws RemoteException {
if (mMediaList.size() == 0) {
load(mediaPathList, 0);
diff --git a/vlc-android/src/org/videolan/vlc/AudioServiceController.java b/vlc-android/src/org/videolan/vlc/AudioServiceController.java
index 9470d48..3818862 100644
--- a/vlc-android/src/org/videolan/vlc/AudioServiceController.java
+++ b/vlc-android/src/org/videolan/vlc/AudioServiceController.java
@@ -235,6 +235,13 @@ public class AudioServiceController implements IAudioPlayerControl {
updateAudioPlayer();
}
+ public void showWithoutParse(String u) {
+ remoteProcedureCall(mAudioServiceBinder, Void.class, (Void)null, "showWithoutParse",
+ new Class<?>[] { String.class },
+ new Object[] { u } );
+ updateAudioPlayer();
+ }
+
@Override
public String getAlbum() {
return remoteProcedureCall(mAudioServiceBinder, String.class, (String)null, "getAlbum", null, 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 1fa99a2..b6d0278 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 c8788e0..3682379 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 ee8251b..875b8d9 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;
@@ -115,6 +117,12 @@ public class VideoPlayerActivity extends Activity {
private ImageButton mLock;
private ImageButton mSize;
+ /**
+ * For uninterrupted switching between audio and video mode
+ */
+ private boolean mSwitchingView;
+ private boolean mEndReached;
+
// size of the video
private int mVideoHeight;
private int mVideoWidth;
@@ -179,6 +187,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 {
@@ -201,10 +212,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;
@@ -226,7 +242,7 @@ public class VideoPlayerActivity extends Activity {
@Override
protected void onDestroy() {
unregisterReceiver(mBatteryReceiver);
- if (mLibVLC != null) {
+ if (mLibVLC != null && !mSwitchingView) {
mLibVLC.stop();
}
@@ -235,9 +251,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
@@ -374,8 +404,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;
@@ -835,6 +874,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
@@ -844,9 +884,10 @@ public class VideoPlayerActivity extends Activity {
} else if(getIntent().getExtras() != null) {
/* Started from VideoListActivity */
mLocation = getIntent().getExtras().getString("itemLocation");
+ dontParse = getIntent().getExtras().getBoolean("dontParse");
}
- if (mLocation != null && mLocation.length() > 0) {
+ if (mLocation != null && mLocation.length() > 0 && !dontParse) {
mLibVLC.readMedia(mLocation, false);
mSurface.setKeepScreenOn(true);
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