Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to run ExoPlayer behind the web view and UDP streaming support #92

Open
wants to merge 4 commits into
base: 2.0.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@ Cordova media player plugin using Google's ExoPlayer framework.

Please send us links to your cool projects made with this plugin so we can include them on this page!

## Changes in version 2.5.8
- Fix player view placement
- Force ExoPlayer to close when application is destroyed.

## Changes in version 2.5.7
- Support for UDP streams

- Upgraded exoplayer to 2.9.5.

## Changes in version 2.5.6
- Added ability to run ExoPlayer behind the Cordova WebView.

- Upgraded exoplayer to 2.7.2.

## Changes in version 2.5.4
- Added loading progress bar to the top that shows up when player is buffering. This needs to be explicitly turned on with `showBuffering` boolean configuration setting.
- Added ability to change text, buttons and buffering colors using new controller configuration settings `textColor`, `buttonsColor` and `bufferingColor`.
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cordova-plugin-exoplayer",
"version": "2.5.4",
"version": "2.5.8",
"description": "Media player for Cordova using Google's ExoPlayer framework",
"cordova": {
"id": "cordova-plugin-exoplayer",
Expand Down Expand Up @@ -39,4 +39,4 @@
"devDependencies": {
"jshint": "^2.9.0"
}
}
}
5 changes: 3 additions & 2 deletions plugin.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0"
id="cordova-plugin-exoplayer"
version="2.5.4">
version="2.5.8">

<name>ExoPlayer</name>
<description>Cordova Plugin implementing ExoPlayer video/audio player</description>
Expand All @@ -19,7 +19,7 @@
</js-module>

<platform name="android">
<framework src="com.google.android.exoplayer:exoplayer:2.6.1"/>
<framework src="com.google.android.exoplayer:exoplayer:2.9.5"/>
<framework src="com.squareup.picasso:picasso:2.5.2"/>

<config-file target="res/xml/config.xml" parent="/*">
Expand Down Expand Up @@ -49,5 +49,6 @@
<source-file src="src/android/Payload.java" target-dir="src/co/frontyard/cordova/plugin/exoplayer/"/>
<source-file src="src/android/Player.java" target-dir="src/co/frontyard/cordova/plugin/exoplayer/"/>
<source-file src="src/android/Plugin.java" target-dir="src/co/frontyard/cordova/plugin/exoplayer/"/>
<source-file src="src/android/Visibility.java" target-dir="src/co/frontyard/cordova/plugin/exoplayer/"/>
</platform>
</plugin>
4 changes: 4 additions & 0 deletions src/android/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ public boolean isAudioOnly() {
return config.optBoolean("audioOnly");
}

public boolean isRunBehindWebViewMode() {
return config.optBoolean("runBehindWebView");
}

public boolean autoPlay() {
return config.optBoolean("autoPlay", true);
}
Expand Down
6 changes: 6 additions & 0 deletions src/android/LayoutProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 +203,10 @@ public static WindowManager.LayoutParams getDialogLayoutParams(Activity activity

return lp;
}

public static LinearLayout getBackgroundLinearLayout(Activity activity) {
LinearLayout linearLayout = new LinearLayout(activity);
linearLayout.setLayoutParams(new LinearLayout.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT));
return linearLayout;
}
}
96 changes: 90 additions & 6 deletions src/android/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,28 @@ of this software and associated documentation files (the "Software"), to deal

import android.app.*;
import android.content.*;
import android.content.res.Resources;
import android.graphics.Color;
import android.media.*;
import android.net.*;
import android.os.*;
import android.util.*;
import android.util.Log;
import android.view.*;
import android.widget.*;
import com.google.android.exoplayer2.*;

import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.LoadControl;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.extractor.*;
import com.google.android.exoplayer2.extractor.ts.DefaultTsPayloadReaderFactory;
import com.google.android.exoplayer2.extractor.ts.TsExtractor;
import com.google.android.exoplayer2.source.*;
import com.google.android.exoplayer2.source.dash.*;
import com.google.android.exoplayer2.source.hls.*;
Expand All @@ -48,6 +62,9 @@ of this software and associated documentation files (the "Software"), to deal
import org.apache.cordova.*;
import org.json.*;

import static com.google.android.exoplayer2.ExoPlayer.STATE_ENDED;
import static com.google.android.exoplayer2.extractor.ts.TsExtractor.MODE_SINGLE_PMT;

public class Player {
public static final String TAG = "ExoPlayerPlugin";
private final Activity activity;
Expand All @@ -60,6 +77,7 @@ public class Player {
private int controllerVisibility;
private boolean paused = false;
private AudioManager audioManager;
private LinearLayout backgroundLinearLayout;

public Player(Configuration config, Activity activity, CallbackContext callbackContext, CordovaWebView webView) {
this.config = config;
Expand Down Expand Up @@ -106,7 +124,7 @@ public void onPositionDiscontinuity(int reason) {
public void onRepeatModeChanged(int newRepeatMode) {
// Need to see if we want to send this to Cordova.
}

@Override
public void onSeekProcessed() {
}
Expand All @@ -116,7 +134,7 @@ public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
}

@Override
public void onTimelineChanged(Timeline timeline, Object manifest) {
public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
JSONObject payload = Payload.timelineChangedEvent(Player.this.exoPlayer, timeline, manifest);
new CallbackResponse(Player.this.callbackContext).send(PluginResult.Status.OK, payload, true);
}
Expand Down Expand Up @@ -203,11 +221,35 @@ else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {

public void createPlayer() {
if (!config.isAudioOnly()) {
createDialog();
if (config.isRunBehindWebViewMode()) {
createBehindWebView();
} else {
createDialog();
}
}
preparePlayer(config.getUri());
}

public void createBehindWebView(){
webView.getView().setBackgroundColor(Color.TRANSPARENT);

FrameLayout mainLayout = LayoutProvider.getMainLayout(this.activity);
exoView = LayoutProvider.getExoPlayerView(this.activity, config);
exoView.setControllerVisibilityListener(playbackControlVisibilityListener);
mainLayout.addView(exoView);

backgroundLinearLayout = LayoutProvider.getBackgroundLinearLayout(this.activity);
backgroundLinearLayout.addView(mainLayout);

FrameLayout wrapperFrameLayout = (FrameLayout) this.webView.getView().getParent();
wrapperFrameLayout.addView(backgroundLinearLayout, 0);

exoView.requestFocus();
exoView.setOnTouchListener(onTouchListener);

LayoutProvider.setupController(exoView, activity, config.getController());
}

public void createDialog() {
dialog = new Dialog(this.activity, android.R.style.Theme_Black_NoTitleBar_Fullscreen);
dialog.setOnKeyListener(onKeyListener);
Expand Down Expand Up @@ -289,6 +331,26 @@ private MediaSource getMediaSource(Uri uri, DefaultBandwidthMeter bandwidthMeter
HttpDataSource.Factory httpDataSourceFactory = new DefaultHttpDataSourceFactory(userAgent, bandwidthMeter, connectTimeout, readTimeout, true);
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(this.activity, bandwidthMeter, httpDataSourceFactory);
MediaSource mediaSource;

if (isUdpUri(uri)) {
//Create default UDP Datasource and mediasource
DataSource.Factory factory = new DataSource.Factory() {
@Override
public DataSource createDataSource() {
return new UdpDataSource(3000, 100000);
}
};

ExtractorsFactory tsExtractorFactory = new ExtractorsFactory() {
@Override
public Extractor[] createExtractors() {
return new TsExtractor[]{new TsExtractor(MODE_SINGLE_PMT, new TimestampAdjuster(0), new DefaultTsPayloadReaderFactory())};
}
};
return new ExtractorMediaSource(uri, factory, tsExtractorFactory,null,null);

}

int type = Util.inferContentType(uri);
switch (type) {
case C.TYPE_DASH:
Expand Down Expand Up @@ -338,12 +400,27 @@ private static String inferSubtitleType(Uri uri) {
}
}

private boolean isUdpUri(Uri uri) {
if (uri == null) return false;

return uri.toString().contains("udp://");
}

public void close() {
audioManager.abandonAudioFocus(audioFocusChangeListener);
FrameLayout wrapperFrameLayout = (FrameLayout) this.webView.getView().getParent();
if (exoPlayer != null) {
exoPlayer.removeListener(playerEventListener);
exoPlayer.stop();
exoPlayer.release();
exoPlayer = null;
}

if (backgroundLinearLayout != null) {
backgroundLinearLayout.removeAllViews();
wrapperFrameLayout.removeView(backgroundLinearLayout);
}

if (this.dialog != null) {
dialog.dismiss();
}
Expand All @@ -368,6 +445,13 @@ public void playPause() {
}
}

public void setPlayWhenReady(Boolean state) {
if (null != exoPlayer) {
paused = state;
exoPlayer.setPlayWhenReady(state);
}
}

private void pause() {
if (null != exoPlayer) {
paused = true;
Expand Down Expand Up @@ -406,7 +490,7 @@ public JSONObject seekBy(long timeMillis) {

public JSONObject getPlayerState() {
return Payload.stateEvent(exoPlayer,
null != exoPlayer ? exoPlayer.getPlaybackState() : SimpleExoPlayer.STATE_ENDED,
null != exoPlayer ? exoPlayer.getPlaybackState() : STATE_ENDED,
Player.this.controllerVisibility == View.VISIBLE);
}

Expand All @@ -432,5 +516,5 @@ private void sendError(String msg) {
Log.e(TAG, msg);
JSONObject payload = Payload.playerErrorEvent(Player.this.exoPlayer, null, msg);
new CallbackResponse(Player.this.callbackContext).send(PluginResult.Status.ERROR, payload, true);
}
}
}
57 changes: 57 additions & 0 deletions src/android/Plugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,16 @@ of this software and associated documentation files (the "Software"), to deal
*/
package co.frontyard.cordova.plugin.exoplayer;

import android.graphics.Color;
import android.net.*;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import org.apache.cordova.*;
import org.json.*;

public class Plugin extends CordovaPlugin {
private String TAG = "EXOPLAYER";
private Player player;

@Override
Expand All @@ -49,6 +53,52 @@ public void run() {
});
return true;
}
else if (action.equals("setWebViewVisibility")) {
cordova.getActivity().runOnUiThread(new Runnable() {
public void run() {
String visibilityString = data.optString(0);
Visibility webViewVisibility = Visibility.VISIBLE;
try {
webViewVisibility = Visibility.valueOf(visibilityString);
} catch (IllegalStateException e) {
Log.d(TAG, "Inappropriate visibility, using VISIBLE as default");
}
webView.getView().setVisibility(webViewVisibility.getValue());
new CallbackResponse(callbackContext).send(PluginResult.Status.NO_RESULT, true);
}
});
return true;
}
else if (action.equals("setWebViewBackgroundColor")) {
cordova.getActivity().runOnUiThread(new Runnable() {
public void run() {
String colorString = data.optString(0);
int parsedColor = Color.TRANSPARENT;
try {
parsedColor = Color.parseColor(colorString);
} catch (IllegalStateException e) {
Log.d(TAG, "Can't parse color, using TRANSPARENT as default");
}
webView.getView().setBackgroundColor(parsedColor);
new CallbackResponse(callbackContext).send(PluginResult.Status.NO_RESULT, true);
}
});
return true;
}
else if (action.equals("setPlayWhenReady")) {
if (self.player == null) {
return false;
}
cordova.getActivity().runOnUiThread(new Runnable() {
public void run() {
Boolean state = data.optBoolean(0);
self.player.setPlayWhenReady(state);
new CallbackResponse(callbackContext).send(PluginResult.Status.NO_RESULT, true);
}
});

return true;
}
else if (action.equals("setStream")) {
if (self.player == null) {
return false;
Expand Down Expand Up @@ -186,4 +236,11 @@ public void run() {
return false;
}
}

public void onDestroy() {
if (this.player != null) {
this.player.close();
}
}

}
24 changes: 24 additions & 0 deletions src/android/Visibility.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package co.frontyard.cordova.plugin.exoplayer;

import android.view.View;

public enum Visibility {
VISIBLE("VISIBLE", View.VISIBLE),
INVISIBLE("INVISIBLE", View.INVISIBLE),
GONE("GONE", View.GONE);

private final String key;
private final Integer value;

Visibility(String key, Integer value) {
this.key = key;
this.value = value;
}

public String getKey() {
return key;
}
public Integer getValue() {
return value;
}
}
14 changes: 14 additions & 0 deletions www/exoplayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,19 @@ module.exports = {
},
close: function (successCallback, errorCallback) {
cordova.exec(successCallback, errorCallback, "ExoPlayer", "close", []);
},
setWebViewVisibility: function (parameters, successCallback, errorCallback) {
cordova.exec(successCallback, errorCallback, "ExoPlayer", "setWebViewVisibility", [parameters]);
},
webViewVisibility: {
VISIBLE: "VISIBLE",
INVISIBLE: "INVISIBLE",
GONE: "GONE"
},
setWebViewBackgroundColor: function (parameters, successCallback, errorCallback) {
cordova.exec(successCallback, errorCallback, "ExoPlayer", "setWebViewBackgroundColor", [parameters]);
},
setPlayWhenReady: function (parameters, successCallback, errorCallback) {
cordova.exec(successCallback, errorCallback, "ExoPlayer", "setPlayWhenReady", [parameters]);
}
};