/*
* Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package tv.danmaku.ijk.media.exo;
import android.content.Context;
import android.net.Uri;
import android.view.Surface;
import android.view.SurfaceHolder;
import com.google.android.exoplayer.ExoPlayer;
import com.google.android.exoplayer.util.Util;
import java.io.FileDescriptor;
import java.util.Map;
import tv.danmaku.ijk.media.exo.demo.EventLogger;
import tv.danmaku.ijk.media.exo.demo.player.DemoPlayer;
import tv.danmaku.ijk.media.exo.demo.player.DemoPlayer.RendererBuilder;
import tv.danmaku.ijk.media.exo.demo.player.ExtractorRendererBuilder;
import tv.danmaku.ijk.media.player.AbstractMediaPlayer;
import tv.danmaku.ijk.media.player.IMediaPlayer;
import tv.danmaku.ijk.media.player.MediaInfo;
import tv.danmaku.ijk.media.player.misc.IjkTrackInfo;
public class IjkExoMediaPlayer extends AbstractMediaPlayer {
private Context mAppContext;
private DemoPlayer mInternalPlayer;
private EventLogger mEventLogger;
private String mDataSource;
private int mVideoWidth;
private int mVideoHeight;
private Surface mSurface;
public static final int TYPE_DASH = 0;
public static final int TYPE_SS = 1;
public static final int TYPE_HLS = 2;
public static final int TYPE_OTHER = 3;
private RendererBuilder mRendererBuilder;
public IjkExoMediaPlayer(Context context) {
mAppContext = context.getApplicationContext();
mDemoListener = new DemoPlayerListener();
mEventLogger = new EventLogger();
mEventLogger.startSession();
}
@Override
public void setDisplay(SurfaceHolder sh) {
if (sh == null)
setSurface(null);
else
setSurface(sh.getSurface());
}
@Override
public void setSurface(Surface surface) {
mSurface = surface;
if (mInternalPlayer != null)
mInternalPlayer.setSurface(surface);
}
@Override
public void setDataSource(Context context, Uri uri) {
mDataSource = uri.toString();
mRendererBuilder = new ExtractorRendererBuilder(context, getUserAgent(), uri);
}
@Override
public void setDataSource(Context context, Uri uri, Map<String, String> headers) {
// TODO: handle headers
setDataSource(context, uri);
}
@Override
public void setDataSource(String path) {
setDataSource(mAppContext, Uri.parse(path));
}
@Override
public void setDataSource(FileDescriptor fd) {
// TODO: no support
throw new UnsupportedOperationException("no support");
}
@Override
public String getDataSource() {
return mDataSource;
}
@Override
public void prepareAsync() throws IllegalStateException {
if (mInternalPlayer != null)
throw new IllegalStateException("can't prepare a prepared player");
mInternalPlayer = new DemoPlayer(mRendererBuilder);
mInternalPlayer.addListener(mDemoListener);
mInternalPlayer.addListener(mEventLogger);
mInternalPlayer.setInfoListener(mEventLogger);
mInternalPlayer.setInternalErrorListener(mEventLogger);
if (mSurface != null)
mInternalPlayer.setSurface(mSurface);
mInternalPlayer.prepare();
mInternalPlayer.setPlayWhenReady(false);
}
@Override
public void start() throws IllegalStateException {
if (mInternalPlayer == null)
return;
mInternalPlayer.setPlayWhenReady(true);
}
@Override
public void stop() throws IllegalStateException {
if (mInternalPlayer == null)
return;
mInternalPlayer.release();
}
@Override
public void pause() throws IllegalStateException {
if (mInternalPlayer == null)
return;
mInternalPlayer.setPlayWhenReady(false);
}
@Override
public void setWakeMode(Context context, int mode) {
// FIXME: implement
}
@Override
public void setScreenOnWhilePlaying(boolean screenOn) {
// TODO: do nothing
}
@Override
public IjkTrackInfo[] getTrackInfo() {
// TODO: implement
return null;
}
@Override
public int getVideoWidth() {
return mVideoWidth;
}
@Override
public int getVideoHeight() {
return mVideoHeight;
}
@Override
public boolean isPlaying() {
if (mInternalPlayer == null)
return false;
int state = mInternalPlayer.getPlaybackState();
switch (state) {
case ExoPlayer.STATE_BUFFERING:
case ExoPlayer.STATE_READY:
return mInternalPlayer.getPlayWhenReady();
case ExoPlayer.STATE_IDLE:
case ExoPlayer.STATE_PREPARING:
case ExoPlayer.STATE_ENDED:
default:
return false;
}
}
@Override
public void seekTo(long msec) throws IllegalStateException {
if (mInternalPlayer == null)
return;
mInternalPlayer.seekTo(msec);
}
@Override
public long getCurrentPosition() {
if (mInternalPlayer == null)
return 0;
return mInternalPlayer.getCurrentPosition();
}
@Override
public long getDuration() {
if (mInternalPlayer == null)
return 0;
return mInternalPlayer.getDuration();
}
@Override
public int getVideoSarNum() {
return 1;
}
@Override
public int getVideoSarDen() {
return 1;
}
@Override
public void reset() {
if (mInternalPlayer != null) {
mInternalPlayer.release();
mInternalPlayer.removeListener(mDemoListener);
mInternalPlayer.removeListener(mEventLogger);
mInternalPlayer.setInfoListener(null);
mInternalPlayer.setInternalErrorListener(null);
mInternalPlayer = null;
}
mSurface = null;
mDataSource = null;
mVideoWidth = 0;
mVideoHeight = 0;
}
@Override
public void setLooping(boolean looping) {
// TODO: no support
throw new UnsupportedOperationException("no support");
}
@Override
public boolean isLooping() {
// TODO: no support
return false;
}
@Override
public void setVolume(float leftVolume, float rightVolume) {
// TODO: no support
}
@Override
public int getAudioSessionId() {
// TODO: no support
return 0;
}
@Override
public MediaInfo getMediaInfo() {
// TODO: no support
return null;
}
@Override
public void setLogEnabled(boolean enable) {
// do nothing
}
@Override
public boolean isPlayable() {
return true;
}
@Override
public void setAudioStreamType(int streamtype) {
// do nothing
}
@Override
public void setKeepInBackground(boolean keepInBackground) {
// do nothing
}
@Override
public void release() {
if (mInternalPlayer != null) {
reset();
mDemoListener = null;
mEventLogger.endSession();
mEventLogger = null;
}
}
private String getUserAgent() {
return Util.getUserAgent(mAppContext, "IjkExoMediaPlayer");
}
public int getBufferedPercentage() {
if (mInternalPlayer == null)
return 0;
return mInternalPlayer.getBufferedPercentage();
}
private class DemoPlayerListener implements DemoPlayer.Listener {
private boolean mIsPrepareing = false;
private boolean mDidPrepare = false;
private boolean mIsBuffering = false;
public void onStateChanged(boolean playWhenReady, int playbackState)
{
if (mIsBuffering) {
switch (playbackState) {
case ExoPlayer.STATE_ENDED:
case ExoPlayer.STATE_READY:
notifyOnInfo(IMediaPlayer.MEDIA_INFO_BUFFERING_END, mInternalPlayer.getBufferedPercentage());
mIsBuffering = false;
break;
}
}
if (mIsPrepareing) {
switch (playbackState) {
case ExoPlayer.STATE_READY:
notifyOnPrepared();
mIsPrepareing = false;
mDidPrepare = false;
break;
}
}
switch (playbackState) {
case ExoPlayer.STATE_IDLE:
notifyOnCompletion();
break;
case ExoPlayer.STATE_PREPARING:
mIsPrepareing = true;
break;
case ExoPlayer.STATE_BUFFERING:
notifyOnInfo(IMediaPlayer.MEDIA_INFO_BUFFERING_START, mInternalPlayer.getBufferedPercentage());
mIsBuffering = true;
break;
case ExoPlayer.STATE_READY:
break;
case ExoPlayer.STATE_ENDED:
notifyOnCompletion();
break;
default:
break;
}
}
public void onError(Exception e)
{
notifyOnError(IMediaPlayer.MEDIA_ERROR_UNKNOWN, IMediaPlayer.MEDIA_ERROR_UNKNOWN);
}
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
float pixelWidthHeightRatio)
{
mVideoWidth = width;
mVideoHeight = height;
notifyOnVideoSizeChanged(width, height, 1, 1);
if (unappliedRotationDegrees > 0)
notifyOnInfo(IMediaPlayer.MEDIA_INFO_VIDEO_ROTATION_CHANGED, unappliedRotationDegrees);
}
}
private DemoPlayerListener mDemoListener;
}