Working on media browser type of application where I read files from USB with custom drivers. File expose by NanoHTTPServer. Not important point here but just for information.
The following code logic works pretty well but it looks broken and start struggling with fast scrolling.
Is there any way to load videos frame faster and avoid hanging and delay in frames. Is there any flaws in this code? As I am using rxjava.
#Override
public void onBindViewHolder(BasicHolder holder,final int position) {
...
if(multiMedia.isImage()){
Glide.with(context)
.load(multiMedia.getUrl())
.crossFade()
.into(holder.imageThumbnail);
}else{
Bitmap bitmap = mThumbnailsCache.get(multiMedia.getUrl()+THUMBNAIL_AT_PERCENT);
if(bitmap == null || multiMedia.getLength() == 0){
//check for already running task
if(holder.imageThumbnail.getTag(R.string.tag_for_video) == null){
loadVideoThumbnail(holder,multiMedia,position);
}
}else {
holder.imageThumbnail.setImageBitmap(bitmap);
}
}
....
//this method will trigger rxjava task on io and set back results emitted by
private void loadVideoThumbnail(final BasicHolder holder, final MultiMedia multiMedia,final int position){
final SoftReference<BasicHolder> mHolderRef = new SoftReference<>(holder);
final Subscription sub = extractFrameAndDuration(multiMedia.getUrl(), THUMBNAIL_AT_PERCENT)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(emittedObject -> {
if (emittedObject == null || mHolderRef.get() == null) return;
if (mHolderRef.get().getLayoutPosition() != position) {
notifyItemChanged(position);
return;
}
if (emittedObject instanceof Bitmap) {
ImageView imageThumbnail = mHolderRef.get().imageThumbnail;
if (imageThumbnail.getTag(R.string.tag_for_video) != null) {
imageThumbnail.setTag(R.string.tag_for_video, null);
imageThumbnail.setImageBitmap((Bitmap) emittedObject);
}
} else {
multiMedia.setLength((long)emittedObject);
TextView textInfo = mHolderRef.get().textInfo;
if (textInfo.getTag() == Filetype.MOVIE) {
mHolderRef.get().viewHighlight.setData(multiMedia.getSavedClips(),multiMedia.getLength());
textInfo.setText(VideoUtils.getTotalClipDuration((long) emittedObject, App.context));
}
}
}, Throwable::printStackTrace);
if(reqSubQueue.size() > 8){
//cancel previous requests
reqSubQueue.pop().unsubscribe();
}
reqSubQueue.add(sub);
holder.imageThumbnail.setTag(R.string.tag_for_video,sub);
}
...
//this method will get thumbnail from video url
private Observable<Object> extractFrameAndDuration(String url, final long atPercent){
return Observable.create(subscriber -> {
final MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
subscriber.add(new Subscription() {
#Override
public void unsubscribe() {
//this will actually cancel loading
Observable.fromCallable(() -> {
mediaMetadataRetriever.release();
return true;
}).subscribeOn(Schedulers.io())
.subscribe();
}
#Override
public boolean isUnsubscribed() {
return false;
}
});
try{
mediaMetadataRetriever.setDataSource(url, new HashMap<>());
String durationString = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
final long duration = Long.parseLong(TextUtils.isEmpty(durationString)? "0": durationString);
//cache duration
clipsDurations.put(url,duration);
subscriber.onNext(duration);
long timeUs = (long) (duration / 100.0 * atPercent);
if(timeUs > duration) timeUs = duration;
Bitmap bitmap = mThumbnailsCache.get(url+THUMBNAIL_AT_PERCENT);
if(bitmap == null){
//1 milli sec = 1000 microseconds
bitmap = MediaUtils.getFrameFromVideoAt(mediaMetadataRetriever, timeUs * 1000);
if(bitmap != null){
bitmap = MediaUtils.resize(bitmap,thumbSize,thumbSize);
mThumbnailsCache.put(url+THUMBNAIL_AT_PERCENT,bitmap);
subscriber.onNext(bitmap);
}
}else {
subscriber.onNext(bitmap);
}
}
catch (Exception e){
e.printStackTrace();
}
finally{
if (mediaMetadataRetriever != null){
mediaMetadataRetriever.release();
}
}
//remove item from queue
subscriber.onCompleted();
reqSubQueue.remove(subscriber);
});
}
All requests get canceled in Activity.onStop. reqSubQueue is ArrayDeque
Edit method description is added
I am sure some pro-developers will find out flaws.
Related
I am creating a class witch loads up a few sounds. However, isPlaying keeps on throwing an exception after a while and then stops playing that particular sound permanently, while other sounds keep playing OK.
public class MySound {
int m_IdMyId;
int m_ResId;
boolean m_IsLoaded;
MediaPlayer m_Media;
public MySound(int idMyId, int resId){
m_IdMyId = idMyId;
m_ResId = resId;
m_IsLoaded = false;
m_Media = null;
}
}
In this m_IdMyId is just an id for my game. m_ResId is something like R.raw.mysound1. m_IsLoaded I think is automatically set to true as I am loading synconously. m_Media is the MediaPlayer object.
I am calling stop() very regularly, as it is a game and I need to check every second or so to make sure certain sounds are stopped. It is here that it throws an exception when snd.m_Media.isPlaying() is called.
I cannot seem to access e to see what the error is.
Also I would like to know how I can set m_IsLoaded correctly. How do I know when the sound is fully loaded and ready to use?
Here is my management class:
public class MySoundManager {
MainActivity m_Context;
ArrayList<MySound> mySounds;
public MySoundManager(MainActivity context) {
m_Context = context;
mySounds = new ArrayList<MySound>();
mySounds.add(new MySound(8, R.raw.mysound1));
mySounds.add(new MySound(10, R.raw.mysound2));
mySounds.add(new MySound(22, R.raw.mysound3));
mySounds.add(new MySound(100, R.raw.click));
mySounds.add(new MySound(101, R.raw.error));
for(MySound mysound : mySounds) {
mysound.m_Media = MediaPlayer.create(m_Context, mysound.m_ResId); // no need to call prepare(); create() does that for you
mysound.m_IsLoaded = true;
}
}
// I call this when the main thread calls onResume
public void onResume(){
for(MySound mysound : mySounds) {
if(mysound.m_Media == null) {
mysound.m_Media = MediaPlayer.create(m_Context, mysound.m_ResId); // no need to call prepare(); create() does that for you
mysound.m_IsLoaded = true;
}
}
}
// I call this when the main thread calls onPause
public void onPause(){
for(MySound mysound : mySounds) {
if(mysound.m_Media != null) {
mysound.m_Media.stop();
mysound.m_Media.release();
mysound.m_Media = null;
}
}
}
public boolean IsAllLoaded(){
for(MySound mysound : mySounds) {
if(!mysound.m_IsLoaded) return false;
}
return true;
}
public MySound FindMySoundByIdMyId(int idMyId){
try {
for(MySound mysound : mySounds) {
if (mysound.m_IdMyId == idMyId) return mysound;
}
}catch(Exception e) {
MySound snd;
snd = null; // ToDo
}
return null;
}
public void play(int idMyId){
MySound snd;
try{
if((snd = FindMySoundByIdMyId(idMyId)) != null)
snd.m_Media.start();
}catch(IllegalStateException e) {
snd = null; // ToDo
}
}
public void pause(int idMyId){
MySound snd;
try{
if((snd = FindMySoundByIdMyId(idMyId)) != null &&
snd.m_Media.isPlaying())
snd.m_Media.pause();
}catch(IllegalStateException e) {
snd = null; // ToDo
}
}
public void pauseAll(){
try{
for (MySound mysound : mySounds) {
if(mysound.m_Media.isPlaying())
mysound.m_Media.pause();
}
}catch(IllegalStateException e) {
MySound snd;
snd = null; // ToDo
}
}
public boolean isPlaying(int idMyId, MySound[] fill){
MySound snd;
fill[0] = null;
try{
if((snd = FindMySoundByIdMyId(idMyId)) != null){
fill[0] = snd;
return snd.m_Media.isPlaying();
}
}catch(IllegalStateException e) {
snd = null; // ToDo
}
return false;
}
public void stop(int idMyId){
MySound snd;
try{
if((snd = FindMySoundByIdMyId(idMyId)) != null &&
snd.m_Media.isPlaying())
snd.m_Media.stop();
}catch(IllegalStateException e) {
snd = null; // ToDo
}
}
// The str is in the format
// number id, 1 = on 0 = off,dont play if this id playing;
public void PlaySound(String str) {
boolean isplaying;
int i, len, id, idDontPlay, milliNow;
String[] strARR = str.split(";");
String[] strARR2;
Integer[] tmpIntARR;
ArrayList<Integer[]> onARR = new ArrayList<Integer[]>();
ArrayList<Integer> offARR = new ArrayList<Integer>();
MySound snd;
for (i = 0, len = strARR.length; i < len; i++) {
if(strARR[i].length() <= 0) continue;
if((strARR2 = strARR[i].split(",")) != null &&
strARR2.length >= 3 &&
strARR2[0].length() > 0 &&
strARR2[1].length() > 0 &&
strARR2[2].length() > 0){
id = Integer.parseInt(strARR2[0]);
idDontPlay = Integer.parseInt(strARR2[2]);
tmpIntARR = new Integer[2];
tmpIntARR[0] = id;
tmpIntARR[1] = idDontPlay;
if(Integer.parseInt(strARR2[1]) == 1){
onARR.add(tmpIntARR);
} else offARR.add(id);
}
}
// Turn off all sounds that need to be turned off
for (i=0,len=offARR.size();i<len;i++) {
id = offARR.get(i);
stop(id);
}
// Turn all sounds that need to be turned on,
// but only if the sound that blocks a new sound is not playing
for (i=0,len=onARR.size();i<len;i++) {
tmpIntARR = onARR.get(i);
id = tmpIntARR[0];
idDontPlay = tmpIntARR[1];
// We dont play if the idDontPlay sound is already playing
if((snd = FindMySoundByIdMyId(idDontPlay)) != null &&
snd.m_Media.isPlaying())
continue;
if((snd = FindMySoundByIdMyId(id)) != null){
isplaying = snd.m_Media.isPlaying();
milliNow = snd.m_Media.getCurrentPosition();
if(milliNow > (snd.m_Media.getDuration() - 1000) ||
(!isplaying && milliNow > 0)){
snd.m_Media.seekTo(0); // Half a second inside
}
if(!isplaying) snd.m_Media.start();
}
}
}
}
Creating a MediaPlayer instance for every sound is not a good practice to get low latency, especially for short clips. MediaPlayer is for longer clips such as Music files it uses large buffer so, larger buffer means high latency. Also, there is AudioFocus mechanism on Android that may interfere your sound playing session. So, I strongly recommend you to use SoundPool to play short clips like game sounds.
I'm trying to implement a fast-forward and rewind actions using PlaybackControlsRow using Leanback library for Android TV, however I can't find any method to detect a long press on these buttons. My current implementation is simple, only does seeking for 10 seconds on one click:
private void setupRows() {
final ClassPresenterSelector ps = new ClassPresenterSelector();
final PlaybackControlsRowPresenter playbackControlsRowPresenter =
new PlaybackControlsRowPresenter(new DescriptionPresenter());
playbackControlsRowPresenter.setOnActionClickedListener(action -> {
if (action.getId() == playPauseAction.getId()) {
togglePlayback(playPauseAction.getIndex() == PlayPauseAction.PLAY);
} else if (action.getId() == fastForwardAction.getId()) {
fastForward();
return;
} else if (action.getId() == rewindAction.getId()) {
rewind();
return;
}
if (action instanceof PlaybackControlsRow.MultiAction) {
((PlaybackControlsRow.MultiAction) action).nextIndex();
notifyChanged(action);
}
});
ps.addClassPresenter(PlaybackControlsRow.class, playbackControlsRowPresenter);
ps.addClassPresenter(ListRow.class, new ListRowPresenter());
rowsAdapter = new ArrayObjectAdapter(ps);
updatePlaybackControlsRow();
setAdapter(rowsAdapter);
}
private void fastForward() {
((PlaybackActivity) getActivity()).onFragmentFastForward();
final int currentTime = ((PlaybackActivity) getActivity()).getPosition();
playbackControlsRow.setCurrentTime(currentTime);
}
private void rewind() {
((PlaybackActivity) getActivity()).onFragmentRewind();
final int currentTime = ((PlaybackActivity) getActivity()).getPosition();
playbackControlsRow.setCurrentTime(currentTime);
}
In PlaybackActivity:
public void onFragmentFastForward() {
// Fast forward 10 seconds.
videoView.seekTo(videoView.getCurrentPosition() + (10 * 1000));
}
public void onFragmentRewind() {
videoView.seekTo(videoView.getCurrentPosition() - (10 * 1000));
}
Is it possible to implement fast-forward and rewind on long press of actions, like key-up/key-down events on the action buttons?
After looking for other solutions, seems that PlaybackControlsRowPresenter is designed in the way that it should have no long presses, but instead increasing/reducing speed by the number of clicks on fast-forward/rewind buttons. It can be clearly seen from internal constructor implementation of PlaybackControlsRowPresenter.FastForwardAction and PlaybackControlsRowPresenter.RewindAction classes:
/**
* Constructor
* #param context Context used for loading resources.
* #param numSpeeds Number of supported fast forward speeds.
*/
public FastForwardAction(Context context, int numSpeeds) {
So, as the result, my solution for now is increasing/reducing a speed by each click on the fast-forward/rewind buttons (which is done on UI by default). After that, when I click on play - it just resumes video from the seeked point.
UPDATE (updated parts of code):
private void setupRows() {
final ClassPresenterSelector ps = new ClassPresenterSelector();
final PlaybackControlsRowPresenter playbackControlsRowPresenter =
new PlaybackControlsRowPresenter(new DescriptionPresenter());
playbackControlsRowPresenter.setOnActionClickedListener(action -> {
if (action.getId() == playPauseAction.getId()) {
togglePlayback(playPauseAction.getIndex() == PlayPauseAction.PLAY);
((PlaybackControlsRow.MultiAction) action).nextIndex();
notifyChanged(action);
} else if (action.getId() == fastForwardAction.getId()) {
if (currentSpeed <= MAX_SPEED) {
currentSpeed++;
showTogglePlayback(false, true);
if (currentSpeed < 0) {
prevRewind();
} else if (currentSpeed > 0) {
nextFastForward();
} else {
currentSpeed++;
prevRewind();
nextFastForward();
}
((PlaybackActivity) getActivity()).seek(currentSpeed);
}
} else if (action.getId() == rewindAction.getId()) {
if (currentSpeed >= MIN_SPEED) {
currentSpeed--;
showTogglePlayback(false, true);
if (currentSpeed > 0) {
prevFastForward();
} else if (currentSpeed < 0) {
nextRewind();
} else {
currentSpeed--;
prevFastForward();
nextRewind();
}
((PlaybackActivity) getActivity()).seek(currentSpeed);
}
} else if (action.getId() == R.id.lb_control_picture_in_picture &&
PlaybackActivity.supportsPictureInPicture(getActivity())) {
getActivity().enterPictureInPictureMode();
}
});
ps.addClassPresenter(PlaybackControlsRow.class, playbackControlsRowPresenter);
ps.addClassPresenter(ListRow.class, new ListRowPresenter());
rowsAdapter = new ArrayObjectAdapter(ps);
updatePlaybackControlsRow();
setAdapter(rowsAdapter);
}
private void prevFastForward() {
fastForwardAction.setIndex(fastForwardAction.getIndex() - 1);
notifyChanged(fastForwardAction);
}
private void nextFastForward() {
fastForwardAction.nextIndex();
notifyChanged(fastForwardAction);
}
private void prevRewind() {
rewindAction.setIndex(rewindAction.getIndex() - 1);
notifyChanged(rewindAction);
}
private void nextRewind() {
rewindAction.nextIndex();
notifyChanged(rewindAction);
}
My project has a demand, need to constantly read the bar code data, like a commodity supermarket scanner with bar code scanning guns, then data into a keypad, but encountered a problem, for a long time continuously scanning, CPU usage will be very high, even reached 95%, I have set the thread to sleep in a loop, but failed to solve this problem.
I have been asking for this problem, but it may be too messy code, affecting everyone to read, and now simplify the code, I hope you can help me, thank you very much;
Sometimes a few hours on the CPU scan occupy too high, but sometimes a few days there. Grab logcat log found the sleep method sometimes is not executed, if not continuous execution will cause CPU use rate is too high, but I don't know why the sleep method will not perform .
private void startReceive() {
stopReceive = false;
new Thread(new Runnable() {
#Override
public void run() {
int timeout = 1000;
while (!stopReceive) {
if (mUsbDeviceConnection != null) {
try {
byte[] receiveBytes = new byte[64];
int value = mUsbDeviceConnection.bulkTransfer(mUsbEndpoint, receiveBytes,
receiveBytes.length, timeout);
if (value > 0) {
for (int i = 2; !stopReceive && i < receiveBytes.length; i++) {
byte b = receiveBytes[i];
if (b != 0) {
result += new String(new byte[]{b});
}
if (!stopReceive && !result.equals("") && result != null) {
Runtime.getRuntime().exec("input text " + result);
}
}
}
Thread.sleep(100);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
}).start();
}
This seemd to be a huge thread running on the main-thread which will drastically slow down the performance of the device.
Big operations you should instead run asynchronously, which means that it will run in the background-thread and not affect the UI-thread which is the issue right now:
Here's a example of how the implementation would look like:
private class LongOperation extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
// StartReceive code..
stopReceive = false;
new Thread(new Runnable() {
#Override
public void run() {
int timeout = 1000;
while (!stopReceive) {
if (mUsbDeviceConnection != null) {
try {
byte[] receiveBytes = new byte[64];
int value = mUsbDeviceConnection.bulkTransfer(mUsbEndpoint, receiveBytes,
receiveBytes.length, timeout);
if (value > 0) {
for (int i = 2; !stopReceive && i < receiveBytes.length; i++) {
byte b = receiveBytes[i];
if (b != 0) {
result += new String(new byte[]{b});
}
if (!stopReceive && !result.equals("") && result != null) {
Runtime.getRuntime().exec("input text " + result);
}
}
}
Thread.sleep(100);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
}).start();
return "Done";
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
// We're done
}
#Override
protected void onPreExecute() {
// Before starting operation
}
#Override
protected void onProgressUpdate(Void... values) {
}
}
How to start the thread:
LongOperation longOp = new LongOperation();
longOp.execute();
Read more: AsyncTask Android example
You should better look this post and try to find which method consume more system resource: https://stackoverflow.com/a/14688291/6176003
I have just started exploring google-play-services-turnbased APIs. Till now I have been successful in creating a match. But from the documentation I haven't been able to figure out how to player's score after he completes his turn.
This is my onClickStartMatch method.
public void onStartMatchClicked() {
Intent intent =
Games.TurnBasedMultiplayer.getSelectOpponentsIntent(mHelper.getApiClient(), 1, 7, true);
startActivityForResult(intent, RC_SELECT_PLAYERS);
}
This is my onActivityResult method in my main activity class.
if (request == RC_SELECT_PLAYERS) {
if (response != RESULT_OK) {
// user canceled
return;
}
// Get the invitee list.
final ArrayList<String> invitees =
data.getStringArrayListExtra(Games.EXTRA_PLAYER_IDS);
// Get auto-match criteria.
Bundle autoMatchCriteria = null;
int minAutoMatchPlayers = data.getIntExtra(
Multiplayer.EXTRA_MIN_AUTOMATCH_PLAYERS, 0);
int maxAutoMatchPlayers = data.getIntExtra(
Multiplayer.EXTRA_MAX_AUTOMATCH_PLAYERS, 0);
if (minAutoMatchPlayers > 0) {
autoMatchCriteria = RoomConfig.createAutoMatchCriteria(
minAutoMatchPlayers, maxAutoMatchPlayers, 0);
} else {
autoMatchCriteria = null;
}
TurnBasedMatchConfig tbmc = TurnBasedMatchConfig.builder()
.addInvitedPlayers(invitees)
.setAutoMatchCriteria(autoMatchCriteria)
.build();
// Create and start the match.
Games.TurnBasedMultiplayer
.createMatch(mHelper.getApiClient(), tbmc)
.setResultCallback(new MatchInitiatedCallback());
}
This is my MatchInitiatedCallback class
public class MatchInitiatedCallback implements
ResultCallback<TurnBasedMultiplayer.InitiateMatchResult>,OnTurnBasedMatchUpdateReceivedListener {
#Override
public void onResult(TurnBasedMultiplayer.InitiateMatchResult result) {
// Check if the status code is not success.
Status status = result.getStatus();
if (status.isSuccess()) {
Log.d("turnbased","Turn Based Match Initiated successfully with result: "+status.getStatusMessage());
return;
}
TurnBasedMatch match = result.getMatch();
// If this player is not the first player in this match, continue.
if (match.getData() != null) {
showTurnUI(match);
return;
}
// Otherwise, this is the first player. Initialize the game state.
initGame(match);
// Let the player take the first turn
showTurnUI(match);
}
public void showTurnUI(TurnBasedMatch match){
if(match.getStatus() == TurnBasedMatch.MATCH_STATUS_ACTIVE){
if(match.getTurnStatus() == TurnBasedMatch.MATCH_TURN_STATUS_MY_TURN){
turnBasedMatchData = match.getData();
Games.TurnBasedMultiplayer.takeTurn(mHelper.getApiClient(),match.getMatchId(), "score:400".getBytes(Charset.forName("UTF-16")),null).setResultCallback(updateMatchResult());
}
}
}
public void initGame(TurnBasedMatch match){
Games.TurnBasedMultiplayer.takeTurn(mHelper.getApiClient(),match.getMatchId(),"score:605".getBytes(Charset.forName("UTF-16")),match.getParticipantId(Games.Players.getCurrentPlayerId(mHelper.getApiClient()))).setResultCallback(updateMatchResult());
}
public ResultCallback<TurnBasedMultiplayer.UpdateMatchResult> updateMatchResult(){
return null;
}
#Override
public void onTurnBasedMatchReceived(TurnBasedMatch turnBasedMatch) {
Log.d("turn-based","Player played his turn");
}
#Override
public void onTurnBasedMatchRemoved(String s) {
}
}
}
Also it would helpful if some can properly explain how to continue a game a game from start and when to submit score and how.
Figured it out. This is how you can do it.
public byte[] persist() {
JSONObject retVal = new JSONObject();
try {
retVal.put("turnCounter", 2);
retVal.put("score1",100);
retVal.put("score2",200);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String st = retVal.toString();
Log.d(TAG, "==== PERSISTING\n" + st);
return st.getBytes(Charset.forName("UTF-8"));
}
Games.TurnBasedMultiplayer.takeTurn(mHelper.getApiClient(),match.getMatchId(),persist(),null).setResultCallback(updateMatchResult());
Is there an onFinished listener of some sort? Or do we have to compare the current stream position against the duration of the track?
It's not pretty but you can make this call:
if (mRemoteMediaPlayer.getMediaStatus().getPlayerState() == MediaStatus.PLAYER_STATE_IDLE
&& mRemoteMediaPlayer.getMediaStatus().getIdleReason() == MediaStatus.IDLE_REASON_FINISHED) {
...
}
Prem,
There is currently no callback to register for such event. One alternative (and-not-so-pretty) approach is the following: on the receiver, listen for "ended" event of the media element and send an event back to the sender through a private channel. Another approach is what you suggested: check position against duration. When SDK graduates to general availability, better and cleaner approaches will be available to accomplish what you want.
Here is solution:
You just need to take one more variable mIdleReason.
1) Initialize mIdleReason as
public int mIdleReason=MediaStatus.IDLE_REASON_NONE;
2) Update value at method loadMedia
public void loadMedia(String url, MediaMetadata movieMetadata, CastSession castSession, boolean autoPlay, long position) {
if (castSession == null || !castSession.isConnected()) {
return;
}
MediaInfo mediaInfo = new MediaInfo.Builder(url)
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
.setContentType("videos/m3u8")
.setMetadata(movieMetadata)
.build();
mRemoteMediaClient = castSession.getRemoteMediaClient();
mRemoteMediaClient.addListener(mRemoteMediaClientListener);
mRemoteMediaClient.load(mediaInfo, autoPlay, position);
mIdleReason = MediaStatus.IDLE_REASON_NONE;
}
3) Update value at onStatusUpdate:
private RemoteMediaClient.Listener mRemoteMediaClientListener = new RemoteMediaClient.Listener() {
#Override
public void onStatusUpdated() {
if (mRemoteMediaClient == null || mediaPlayerListener == null) {
return;
}
MediaStatus mediaStatus = mRemoteMediaClient.getMediaStatus();
if (mediaStatus != null) {
int playerStatus = mediaStatus.getPlayerState();
Log.d("PlayerState", "onStatusUpdated() called, progress= "+mSeekBar.getProgress() +", stream duration= "+ mRemoteMediaClient.getStreamDuration()+" mSeekBar.getProgress() == mRemoteMediaClient.getStreamDuration()="+(mSeekBar.getProgress() == mRemoteMediaClient.getStreamDuration()));
Log.d("PlayerState", "onStatusUpdated() called playerStatus="+playerStatus+", idleReason="+mediaStatus.getIdleReason());
if (playerStatus == MediaStatus.PLAYER_STATE_PLAYING) {
mediaPlayerListener.playing();
mIdleReason = MediaStatus.IDLE_REASON_FINISHED;
} else if (playerStatus == MediaStatus.PLAYER_STATE_BUFFERING) {
mediaPlayerListener.buffering();
mIdleReason = MediaStatus.IDLE_REASON_FINISHED;
} else if (playerStatus == MediaStatus.PLAYER_STATE_PAUSED) {
mediaPlayerListener.paused();
} else if (playerStatus == MediaStatus.IDLE_REASON_INTERRUPTED) {
mediaPlayerListener.error();
} else if (playerStatus == MediaStatus.IDLE_REASON_ERROR) {
mediaPlayerListener.error();
}else if(playerStatus == MediaStatus.PLAYER_STATE_IDLE && mediaStatus.getIdleReason() == MediaStatus.IDLE_REASON_FINISHED&& mIdleReason == MediaStatus.IDLE_REASON_FINISHED){
mediaPlayerListener.played();
}
}
}
#Override
public void onMetadataUpdated() {
Log.d("", "onMetadataUpdated: ");
}
#Override
public void onQueueStatusUpdated() {
Log.d("", "onQueueStatusUpdated: ");
}
#Override
public void onPreloadStatusUpdated() {
Log.d("", "onPreloadStatusUpdated: ");
}
#Override
public void onSendingRemoteMediaRequest() {
Log.d("", "onSendingRemoteMediaRequest: ");
}
#Override
public void onAdBreakStatusUpdated() {
Log.d("", "onAdBreakStatusUpdated: ");
}
};