package info.guardianproject.iocipher.player;
import java.io.IOException;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.os.PowerManager;
import android.util.Log;
import android.view.SurfaceHolder;
import android.widget.Toast;
import android.widget.MediaController.MediaPlayerControl;
public class MjpegPlayer extends Thread implements MediaPlayerControl {
private static final String TAG = "MjpegPlayer";
private SurfaceHolder mSurfaceHolder;
private MjpegInputStream mIn = null;
SeekableStream cacheStream = null;
private boolean mRun = false;
private int frameCounter = 0;
private int dispWidth;
private int dispHeight;
private int displayMode;
private long start, fpsstart;
private long pause;
private int duration;
private Bitmap fpsoverlay;
private boolean showFps = false;
public static final int STATE_NOTPLAYING = -1;
public static final int STATE_PLAYING = 0;
public static final int STATE_PAUSE = 1;
public static final int STATE_SEEKED = 2;
public int state = STATE_NOTPLAYING;
private Context mContext;
private PowerManager.WakeLock mWakeLock = null;
private boolean mScreenOnWhilePlaying;
private boolean mStayAwake;
public MjpegPlayer(SurfaceHolder surfaceHolder, Context context) {
mSurfaceHolder = surfaceHolder;
mContext = context;
}
public MjpegPlayer(SurfaceHolder holder, Context context, MjpegInputStream mIn) {
this(holder, context);
setSource(mIn);
}
public void setWakeMode(Context context, int mode) {
boolean washeld = false;
if (mWakeLock != null) {
if (mWakeLock.isHeld()) {
washeld = true;
mWakeLock.release();
}
mWakeLock = null;
}
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(mode|PowerManager.ON_AFTER_RELEASE, MjpegPlayer.class.getName());
mWakeLock.setReferenceCounted(false);
if (washeld) {
mWakeLock.acquire();
}
}
/**
* Control whether we should use the attached SurfaceHolder to keep the
* screen on while video playback is occurring. This is the preferred
* method over {@link #setWakeMode} where possible, since it doesn't
* require that the application have permission for low-level wake lock
* access.
*
* @param screenOn Supply true to keep the screen on, false to allow it
* to turn off.
*/
public void setScreenOnWhilePlaying(boolean screenOn) {
if (mScreenOnWhilePlaying != screenOn) {
mScreenOnWhilePlaying = screenOn;
updateSurfaceScreenOn();
}
}
private void stayAwake(boolean awake) {
if (mWakeLock != null) {
if (awake && !mWakeLock.isHeld()) {
mWakeLock.acquire();
} else if (!awake && mWakeLock.isHeld()) {
mWakeLock.release();
}
}
mStayAwake = awake;
updateSurfaceScreenOn();
}
private Rect destRect(int bmw, int bmh) {
int tempx;
int tempy;
if (displayMode == MjpegView.SIZE_STANDARD) {
tempx = (dispWidth / 2) - (bmw / 2);
tempy = (dispHeight / 2) - (bmh / 2);
return new Rect(tempx, tempy, bmw + tempx, bmh + tempy);
}
if (displayMode == MjpegView.SIZE_BEST_FIT) {
float bmasp = (float) bmw / (float) bmh;
bmw = dispWidth;
bmh = (int) (dispWidth / bmasp);
if (bmh > dispHeight) {
bmh = dispHeight;
bmw = (int) (dispHeight * bmasp);
}
tempx = (dispWidth / 2) - (bmw / 2);
tempy = (dispHeight / 2) - (bmh / 2);
return new Rect(tempx, tempy, bmw + tempx, bmh + tempy);
}
if (displayMode == MjpegView.SIZE_FULLSCREEN) return new Rect(0, 0, dispWidth, dispHeight);
return null;
}
private void updateSurfaceScreenOn() {
if (mSurfaceHolder != null) {
mSurfaceHolder.setKeepScreenOn(mScreenOnWhilePlaying && mStayAwake);
}
}
public void setSurfaceSize(int width, int height) {
synchronized(mSurfaceHolder) {
dispWidth = width;
dispHeight = height;
}
}
public void setDisplay(int dispWidth, int dispHeight, int displayMode) {
this.dispWidth = dispWidth;
this.dispHeight = dispHeight;
this.displayMode = displayMode;
}
public void setDisplayMode(int s) {
displayMode = s;
}
public void run() {
start = System.currentTimeMillis();
fpsstart = System.currentTimeMillis();
Log.v("MjpegView", "Start at " + start);
PorterDuffXfermode mode = new PorterDuffXfermode(PorterDuff.Mode.DST_OVER);
Bitmap bm;
Bitmap bm_pause = null;
Rect destRect = null;
Canvas c = null;
Paint p = new Paint();
String fps = "";
OverlayHandler overlay = null;
while (mRun) {
try {
c = mSurfaceHolder.lockCanvas();
if (overlay == null) overlay = new OverlayHandler(c, p);
synchronized (mSurfaceHolder) {
try {
overlay.setOvltop(5);
bm = mIn.readMjpegFrame();
if (bm == null) {
Log.v(TAG, "Bitmap is null!");
continue;
}
destRect = destRect(bm.getWidth(),bm.getHeight());
if (state == STATE_PLAYING) {
c.drawColor(Color.BLACK);
c.drawBitmap(bm, null, destRect, p);
bm_pause = null;
}
else if (state == STATE_PAUSE){
if (bm_pause == null) bm_pause = bm;
c.drawColor(Color.BLACK);
c.drawBitmap(bm_pause, null, destRect, p);
String time = "T-" + (duration - (int)(pause-start))/1000 + " sec";
Bitmap ovl1 = overlay.makeOverlay("PAUSED");
Bitmap ovl2 = overlay.makeOverlay(time);
overlay.drawOverlay(ovl1);
overlay.drawOverlay(ovl2);
}
else if (state == STATE_SEEKED) {
c.drawColor(Color.BLACK);
c.drawBitmap(bm, null, destRect, p);
bm_pause = null;
Bitmap ovl1 = overlay.makeOverlay("SEEKED");
overlay.drawOverlay(ovl1);
}
//*/
if(showFps) {
//p.setXfermode(mode);
if(fpsoverlay != null) {
overlay.drawOverlay(fpsoverlay);
}
//p.setXfermode(null);
frameCounter++;
if((System.currentTimeMillis() - fpsstart) >= 1000) {
fps = String.valueOf(frameCounter)+"fps";
//Log.v(TAG, "FPS: " + fps);
frameCounter = 0;
fpsstart = System.currentTimeMillis();
fpsoverlay = overlay.makeOverlay(fps);
}
}
//*/
setDuration(System.currentTimeMillis()-start);
} catch (IOException e) {}
}
} finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
synchronized(mSurfaceHolder) {
c = mSurfaceHolder.lockCanvas();
c.drawColor(Color.BLACK);
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
private void setDuration(long l) {
duration = (int) l;
}
public void startPlayback() {
state = STATE_PLAYING;
mRun = true;
this.start();
}
public boolean isPlaying() {
return mRun;
}
public void stopPlayback() {
mRun = false;
boolean retry = true;
while(retry) {
try {
this.join();
retry = false;
} catch (InterruptedException e) {}
}
}
public void pause() {
pause = System.currentTimeMillis();
if (isPlaying() && state==STATE_PLAYING) {
state = STATE_PAUSE;
Log.v(TAG, "new state: pause");
}
else {
state = STATE_PLAYING;
Log.v(TAG, "new state: play");
}
}
public int getCurrentPosition() {
switch (state) {
case STATE_PLAYING:
return duration;
case STATE_PAUSE:
//Log.v(TAG, "CurrentPosition: " + (int)(pause-start));
return (int)(pause-start);
case STATE_SEEKED:
double pos_ratio;
try {
pos_ratio = mIn.getPosition();
Log.v(TAG, "File Position Ratio: " + pos_ratio);
} catch (IOException e) {
Log.v(TAG, "could not get position...");
pos_ratio = 0.5;
e.printStackTrace();
}
return (int) (pos_ratio * duration);
default:
return 0;
}
}
public int getDuration() {
return (int)duration;
}
public void showFps(boolean b) {
showFps = b;
}
public void setSource(MjpegInputStream source) {
mIn = source;
}
public void seekTo(int pos) {
state = STATE_SEEKED;
Toast.makeText(mContext, "seek to: " +pos, Toast.LENGTH_SHORT).show();
double ratio = (double)pos / duration;
Log.v(TAG, "seek to: " + pos + " or better: to " + ratio);
try {
mIn.seekTo(ratio);
} catch (IOException e) {
Log.e(TAG, "Couldn't seek to " + ratio);
e.printStackTrace();
}
}
public void seekToBegin() {
seekTo(0);
}
public void seekToLive() {
state = STATE_PLAYING;
try {
mIn.seekToLive();
} catch (IOException e) {
Log.e(TAG, "couldn't seek to live view");
e.printStackTrace();
}
}
public int getBufferPercentage() {
return 100;
}
public boolean canPause() {
return true;
}
public boolean canSeekBackward() {
return true;
}
public boolean canSeekForward() {
return true;
}
@Override
public int getAudioSessionId() {
return 0;
}
public void release() {
if (mWakeLock != null) mWakeLock.release();
updateSurfaceScreenOn();
// mOnPreparedListener = null;
// mOnBufferingUpdateListener = null;
// mOnCompletionListener = null;
// mOnSeekCompleteListener = null;
// mOnErrorListener = null;
// _release();
}
}