/*
* Copyright (C) 2006 The Android Open Source Project
* Copyright (C) 2013 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.player;
import android.annotation.TargetApi;
import android.content.Context;
import android.media.AudioManager;
import android.media.MediaDataSource;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Build;
import android.text.TextUtils;
import android.view.Surface;
import android.view.SurfaceHolder;
import java.io.FileDescriptor;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.Map;
import tv.danmaku.ijk.media.player.misc.AndroidTrackInfo;
import tv.danmaku.ijk.media.player.misc.IMediaDataSource;
import tv.danmaku.ijk.media.player.misc.ITrackInfo;
import tv.danmaku.ijk.media.player.pragma.DebugLog;
public class AndroidMediaPlayer extends AbstractMediaPlayer {
private final MediaPlayer mInternalMediaPlayer;
private final AndroidMediaPlayerListenerHolder mInternalListenerAdapter;
private String mDataSource;
private MediaDataSource mMediaDataSource;
private final Object mInitLock = new Object();
private boolean mIsReleased;
private static MediaInfo sMediaInfo;
public AndroidMediaPlayer() {
synchronized (mInitLock) {
mInternalMediaPlayer = new MediaPlayer();
}
mInternalMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mInternalListenerAdapter = new AndroidMediaPlayerListenerHolder(this);
attachInternalListeners();
}
public MediaPlayer getInternalMediaPlayer() {
return mInternalMediaPlayer;
}
@Override
public void setDisplay(SurfaceHolder sh) {
synchronized (mInitLock) {
if (!mIsReleased) {
mInternalMediaPlayer.setDisplay(sh);
}
}
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
@Override
public void setSurface(Surface surface) {
mInternalMediaPlayer.setSurface(surface);
}
@Override
public void setDataSource(Context context, Uri uri)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
mInternalMediaPlayer.setDataSource(context, uri);
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
@Override
public void setDataSource(Context context, Uri uri, Map<String, String> headers)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
mInternalMediaPlayer.setDataSource(context, uri, headers);
}
@Override
public void setDataSource(FileDescriptor fd)
throws IOException, IllegalArgumentException, IllegalStateException {
mInternalMediaPlayer.setDataSource(fd);
}
@Override
public void setDataSource(String path) throws IOException,
IllegalArgumentException, SecurityException, IllegalStateException {
mDataSource = path;
Uri uri = Uri.parse(path);
String scheme = uri.getScheme();
if (!TextUtils.isEmpty(scheme) && scheme.equalsIgnoreCase("file")) {
mInternalMediaPlayer.setDataSource(uri.getPath());
} else {
mInternalMediaPlayer.setDataSource(path);
}
}
@TargetApi(Build.VERSION_CODES.M)
@Override
public void setDataSource(IMediaDataSource mediaDataSource) {
releaseMediaDataSource();
mMediaDataSource = new MediaDataSourceProxy(mediaDataSource);
mInternalMediaPlayer.setDataSource(mMediaDataSource);
}
@TargetApi(Build.VERSION_CODES.M)
private static class MediaDataSourceProxy extends MediaDataSource {
private final IMediaDataSource mMediaDataSource;
public MediaDataSourceProxy(IMediaDataSource mediaDataSource) {
mMediaDataSource = mediaDataSource;
}
@Override
public int readAt(long position, byte[] buffer, int offset, int size) throws IOException {
return mMediaDataSource.readAt(position, buffer, offset, size);
}
@Override
public long getSize() throws IOException {
return mMediaDataSource.getSize();
}
@Override
public void close() throws IOException {
mMediaDataSource.close();
}
}
@Override
public String getDataSource() {
return mDataSource;
}
private void releaseMediaDataSource() {
if (mMediaDataSource != null) {
try {
mMediaDataSource.close();
} catch (IOException e) {
e.printStackTrace();
}
mMediaDataSource = null;
}
}
@Override
public void prepareAsync() throws IllegalStateException {
mInternalMediaPlayer.prepareAsync();
}
@Override
public void start() throws IllegalStateException {
mInternalMediaPlayer.start();
}
@Override
public void stop() throws IllegalStateException {
mInternalMediaPlayer.stop();
}
@Override
public void pause() throws IllegalStateException {
mInternalMediaPlayer.pause();
}
@Override
public void setScreenOnWhilePlaying(boolean screenOn) {
mInternalMediaPlayer.setScreenOnWhilePlaying(screenOn);
}
@Override
public ITrackInfo[] getTrackInfo() {
return AndroidTrackInfo.fromMediaPlayer(mInternalMediaPlayer);
}
@Override
public int getVideoWidth() {
return mInternalMediaPlayer.getVideoWidth();
}
@Override
public int getVideoHeight() {
return mInternalMediaPlayer.getVideoHeight();
}
@Override
public int getVideoSarNum() {
return 1;
}
@Override
public int getVideoSarDen() {
return 1;
}
@Override
public boolean isPlaying() {
try {
return mInternalMediaPlayer.isPlaying();
} catch (IllegalStateException e) {
DebugLog.printStackTrace(e);
return false;
}
}
@Override
public void seekTo(long msec) throws IllegalStateException {
mInternalMediaPlayer.seekTo((int) msec);
}
@Override
public long getCurrentPosition() {
try {
return mInternalMediaPlayer.getCurrentPosition();
} catch (IllegalStateException e) {
DebugLog.printStackTrace(e);
return 0;
}
}
@Override
public long getDuration() {
try {
return mInternalMediaPlayer.getDuration();
} catch (IllegalStateException e) {
DebugLog.printStackTrace(e);
return 0;
}
}
@Override
public void release() {
mIsReleased = true;
mInternalMediaPlayer.release();
releaseMediaDataSource();
resetListeners();
attachInternalListeners();
}
@Override
public void reset() {
try {
mInternalMediaPlayer.reset();
} catch (IllegalStateException e) {
DebugLog.printStackTrace(e);
}
releaseMediaDataSource();
resetListeners();
attachInternalListeners();
}
@Override
public void setLooping(boolean looping) {
mInternalMediaPlayer.setLooping(looping);
}
@Override
public boolean isLooping() {
return mInternalMediaPlayer.isLooping();
}
@Override
public void setVolume(float leftVolume, float rightVolume) {
mInternalMediaPlayer.setVolume(leftVolume, rightVolume);
}
@Override
public int getAudioSessionId() {
return mInternalMediaPlayer.getAudioSessionId();
}
@Override
public MediaInfo getMediaInfo() {
if (sMediaInfo == null) {
MediaInfo module = new MediaInfo();
module.mVideoDecoder = "android";
module.mVideoDecoderImpl = "HW";
module.mAudioDecoder = "android";
module.mAudioDecoderImpl = "HW";
sMediaInfo = module;
}
return sMediaInfo;
}
@Override
public void setLogEnabled(boolean enable) {
}
@Override
public boolean isPlayable() {
return true;
}
/*--------------------
* misc
*/
@Override
public void setWakeMode(Context context, int mode) {
mInternalMediaPlayer.setWakeMode(context, mode);
}
@Override
public void setAudioStreamType(int streamtype) {
mInternalMediaPlayer.setAudioStreamType(streamtype);
}
@Override
public void setKeepInBackground(boolean keepInBackground) {
}
/*--------------------
* Listeners adapter
*/
private void attachInternalListeners() {
mInternalMediaPlayer.setOnPreparedListener(mInternalListenerAdapter);
mInternalMediaPlayer
.setOnBufferingUpdateListener(mInternalListenerAdapter);
mInternalMediaPlayer.setOnCompletionListener(mInternalListenerAdapter);
mInternalMediaPlayer
.setOnSeekCompleteListener(mInternalListenerAdapter);
mInternalMediaPlayer
.setOnVideoSizeChangedListener(mInternalListenerAdapter);
mInternalMediaPlayer.setOnErrorListener(mInternalListenerAdapter);
mInternalMediaPlayer.setOnInfoListener(mInternalListenerAdapter);
}
private class AndroidMediaPlayerListenerHolder implements
MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener,
MediaPlayer.OnBufferingUpdateListener,
MediaPlayer.OnSeekCompleteListener,
MediaPlayer.OnVideoSizeChangedListener,
MediaPlayer.OnErrorListener, MediaPlayer.OnInfoListener {
public final WeakReference<AndroidMediaPlayer> mWeakMediaPlayer;
public AndroidMediaPlayerListenerHolder(AndroidMediaPlayer mp) {
mWeakMediaPlayer = new WeakReference<AndroidMediaPlayer>(mp);
}
@Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
AndroidMediaPlayer self = mWeakMediaPlayer.get();
return self != null && notifyOnInfo(what, extra);
}
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
AndroidMediaPlayer self = mWeakMediaPlayer.get();
return self != null && notifyOnError(what, extra);
}
@Override
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
AndroidMediaPlayer self = mWeakMediaPlayer.get();
if (self == null)
return;
notifyOnVideoSizeChanged(width, height, 1, 1);
}
@Override
public void onSeekComplete(MediaPlayer mp) {
AndroidMediaPlayer self = mWeakMediaPlayer.get();
if (self == null)
return;
notifyOnSeekComplete();
}
@Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
AndroidMediaPlayer self = mWeakMediaPlayer.get();
if (self == null)
return;
notifyOnBufferingUpdate(percent);
}
@Override
public void onCompletion(MediaPlayer mp) {
AndroidMediaPlayer self = mWeakMediaPlayer.get();
if (self == null)
return;
notifyOnCompletion();
}
@Override
public void onPrepared(MediaPlayer mp) {
AndroidMediaPlayer self = mWeakMediaPlayer.get();
if (self == null)
return;
notifyOnPrepared();
}
}
}