我几乎尝试了所有方法,但是我无法在循环单个音轨之间实现无间隙音频播放,持续时间为10-15秒。
我尝试过的步骤失败了:
使用不同的音频文件格式.mp3
.wav
.ogg
setLooping(true)
:
MediaPlayer mp1 = MediaPlayer.create(MainActivity.this, R.raw.track1);
mp1.setLooping(true);
mp1.start();
创建两个媒体播放器并使用一个接一个地循环
setOnCompletionListener
同样无法无间隙地循环。
使用setNextMediaPlayer(nextmp)
一些如何工作,但只有两个循环是可能的。在完成前两个循环后,我们必须准备并重新开始。
mp1.start();
mp1.setNextMediaPlayer(mp2);
更新 结果@Jeff Mixon回答: Mediaplayer looping stops with an error Android。 Jeff Mixon工作正常,但之后只有10或20个循环,由于一些垃圾收集问题,Mediaplayers会立即停止离开日志,如下所示。我真的有点困在这里2年了。提前谢谢。
E/MediaPlayer(24311): error (1, -38)
E/MediaPlayer(23256): Error(1,-1007)
E/MediaPlayer(23546): Error (1,-2147483648)
答案 0 :(得分:29)
从我所做的测试来看,这个解决方案运行良好,超过150个循环,13秒160 kbps MP3没有任何问题:
public class LoopMediaPlayer {
public static final String TAG = LoopMediaPlayer.class.getSimpleName();
private Context mContext = null;
private int mResId = 0;
private int mCounter = 1;
private MediaPlayer mCurrentPlayer = null;
private MediaPlayer mNextPlayer = null;
public static LoopMediaPlayer create(Context context, int resId) {
return new LoopMediaPlayer(context, resId);
}
private LoopMediaPlayer(Context context, int resId) {
mContext = context;
mResId = resId;
mCurrentPlayer = MediaPlayer.create(mContext, mResId);
mCurrentPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
mCurrentPlayer.start();
}
});
createNextMediaPlayer();
}
private void createNextMediaPlayer() {
mNextPlayer = MediaPlayer.create(mContext, mResId);
mCurrentPlayer.setNextMediaPlayer(mNextPlayer);
mCurrentPlayer.setOnCompletionListener(onCompletionListener);
}
private MediaPlayer.OnCompletionListener onCompletionListener = new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.release();
mCurrentPlayer = mNextPlayer;
createNextMediaPlayer();
Log.d(TAG, String.format("Loop #%d", ++mCounter));
}
};
}
要使用LoopMediaPlayer
,您只需致电:
LoopMediaPlayer.create(context, R.raw.sample);
答案 1 :(得分:13)
丑陋的概念证明代码,但你会得到这个想法:
// Will need this in the callbacks
final AssetFileDescriptor afd = getResources().openRawResourceFd(R.raw.sample);
// Build and start first player
final MediaPlayer player1 = MediaPlayer.create(this, R.raw.sample);
player1.start();
// Ready second player
final MediaPlayer player2 = MediaPlayer.create(this, R.raw.sample);
player1.setNextMediaPlayer(player2);
player1.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
// When player1 completes, we reset it, and set up player2 to go back to player1 when it's done
mediaPlayer.reset();
try {
mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
mediaPlayer.prepare();
} catch (Exception e) {
e.printStackTrace();
}
player2.setNextMediaPlayer(player1);
}
});
player2.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
// Likewise, when player2 completes, we reset it and tell it player1 to user player2 after it's finished again
mediaPlayer.reset();
try {
mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
mediaPlayer.prepare();
} catch (Exception e) {
e.printStackTrace();
}
player1.setNextMediaPlayer(player2);
}
});
// This loop repeats itself endlessly in this fashion without gaps
这适用于API 19设备和5秒128 kbps MP3。循环中没有间隙。
答案 2 :(得分:5)
至少从KitKat开始,Mattia Maestrini's Answer(对于这个问题)是 唯一的解决方案我发现允许无间隙循环大型(> 1Mb未压缩)音频样本 即可。我试过了:
只需在我的项目中包含Maestrini的LoopMediaPlayer
课程,然后使用MediaPlayer.create()
来电替换我的LoopMediaPlayer.create()
来电,我就可以确保我的.OGG示例无缝循环。因此LoopMediaPlayer
是一个值得称赞的实用且透明的解决方案。
但这种透明度引出了一个问题:一旦我调用MediaPlayer
来电LoopMediaPlayer
次来电,我的实例如何调用MediaPlayer
方法,例如。isPlaying
,{{ 1}}或.pause
? 以下是此问题的解决方案 。可能它可以由比我更熟悉Java的人改进(我欢迎他们的意见),但到目前为止,我发现这是一个可靠的解决方案。
我对Maestrini的课程所做的唯一更改(除了Lint推荐的一些调整)在下面的代码末尾标记;其余的我包括上下文。我的补充是通过在.setVolume
上调用MediaPlayer
来实现LoopMediaPlayer
中mCurrentPlayer
的几种方法。
警告: ,当我实施以下MediaPlayer
的几个有用方法时, 我没有实现所有这些方法。 因此,如果您希望例如调用.attachAuxEffect
,则需要将自己添加为LoopMediaPlayer
的方法,与我添加的内容相同。一定要复制这些方法的原始接口(即参数,抛出和返回):
public class LoopMediaPlayer {
private static final String TAG = LoopMediaPlayer.class.getSimpleName();
private Context mContext = null;
private int mResId = 0;
private int mCounter = 1;
private MediaPlayer mCurrentPlayer = null;
private MediaPlayer mNextPlayer = null;
public static LoopMediaPlayer create(Context context, int resId) {
return new LoopMediaPlayer(context, resId);
}
private LoopMediaPlayer(Context context, int resId) {
mContext = context;
mResId = resId;
mCurrentPlayer = MediaPlayer.create(mContext, mResId);
mCurrentPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
mCurrentPlayer.start();
}
});
createNextMediaPlayer();
}
private void createNextMediaPlayer() {
mNextPlayer = MediaPlayer.create(mContext, mResId);
mCurrentPlayer.setNextMediaPlayer(mNextPlayer);
mCurrentPlayer.setOnCompletionListener(onCompletionListener);
}
private final MediaPlayer.OnCompletionListener onCompletionListener = new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.release();
mCurrentPlayer = mNextPlayer;
createNextMediaPlayer();
Log.d(TAG, String.format("Loop #%d", ++mCounter));
}
};
// code-read additions:
public boolean isPlaying() throws IllegalStateException {
return mCurrentPlayer.isPlaying();
}
public void setVolume(float leftVolume, float rightVolume) {
mCurrentPlayer.setVolume(leftVolume, rightVolume);
}
public void start() throws IllegalStateException {
mCurrentPlayer.start();
}
public void stop() throws IllegalStateException {
mCurrentPlayer.stop();
}
public void pause() throws IllegalStateException {
mCurrentPlayer.pause();
}
public void release() {
mCurrentPlayer.release();
mNextPlayer.release();
}
public void reset() {
mCurrentPlayer.reset();
}
}
答案 3 :(得分:2)
这样的事情应该有效。在res.raw目录中保留同一文件的两个副本。请注意,这只是一个POC而不是优化的代码。我刚测试了它,它按预期工作。让我知道你的想法。
public class MainActivity extends Activity {
MediaPlayer mp1;
MediaPlayer mp2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mp1 = MediaPlayer.create(MainActivity.this, R.raw.demo);
mp2 = MediaPlayer.create(MainActivity.this, R.raw.demo2);
mp1.start();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
int duration = mp1.getDuration();
while (mp1.isPlaying() || mp2.isPlaying()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
duration = duration - 100;
if (duration < 1000) {
if (mp1.isPlaying()) {
mp2.start();
mp1.reset();
mp1 = MediaPlayer.create(MainActivity.this,
R.raw.demo);
duration = mp2.getDuration();
} else {
mp1.start();
mp2.reset();
mp2 = MediaPlayer.create(MainActivity.this,
R.raw.demo2);
duration = mp1.getDuration();
}
}
}
}
});
thread.start();
}
}
答案 4 :(得分:2)
我建议您使用SoundPool API代替MediaPlayer
。
来自官方文件:
SoundPool类管理和播放音频资源 应用
...
可以通过设置非零循环来循环声音 值。值为-1会导致声音永远循环。在这种情况下, 应用程序必须显式调用stop()函数来停止 声音。任何其他非零值将导致声音重复 指定的次数,例如值为3会导致声音播放 共4次。
...
请查看here,了解如何使用SoundPool
。
答案 5 :(得分:1)
出于某种原因,我发现我的&#34; OnCompletion&#34; 事件在尝试循环8秒时总是触发第二晚的一小部分OGG文件。对于遇到此类延迟的任何人,请尝试以下操作。
可以强制排队 &#34; nextMediaPlayer&#34; ,就像以前的解决方案一样,只需发布延迟的Runnable 对于MediaPlayers的处理程序和完全避免在onCompletion 事件中循环。
使用我的160kbps 8秒OGG,最低API 16,这对我来说完美无瑕。
在您的活动/服务中的某个位置,创建一个 HandlerThread&amp;处理强> ...
private HandlerThread SongLooperThread = new HandlerThread("SongLooperThread");
private Handler SongLooperHandler;
public void startSongLooperThread(){
SongLooperThread.start();
Looper looper = SongLooperThread.getLooper();
SongLooperHandler = new Handler(looper){
@Override
public void handleMessage(Message msg){
//do whatever...
}
}
}
public void stopSongLooperThread(){
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2){
SongLooperThread.quit();
} else {
SongLooperThread.quitSafely();
}
}`
... 启动帖子,声明并设置您的MediaPlayers ......
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
startSongLooperThread();
activeSongResID = R.raw.some_loop;
activeMP = MediaPlayer.create(getApplicationContext(), activeSongResID);
activeSongMilliseconds = activeMP.getDuration();
queuedMP = MediaPlayer.create(getApplicationContext(),activeSongResID);
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
stopSongLooperThread();
activeMP.release();
queuedMP.release();
activeMP = null;
queuedMP = null;
}
...创建交换 MediaPlayers的方法......
private void swapActivePlayers(){
Log.v("SongLooperService","MediaPlayer swap started....");
queuedMP.start();
//Immediately get the Duration of the current track, then queue the next swap.
activeSongMilliseconds = queuedMP.getDuration();
SongLooperHandler.postDelayed(timedQueue,activeSongMilliseconds);
Log.v("SongLooperService","Next call queued...");
activeMP.release();
//Swap your active and queued MPs...
Log.v("SongLooperService","MediaPlayers swapping....");
MediaPlayer temp = activeMP;
activeMP = queuedMP;
queuedMP = temp;
//Prepare your now invalid queuedMP...
queuedMP = MediaPlayer.create(getApplicationContext(),activeSongResID);
Log.v("SongLooperService","MediaPlayer swapped.");
}
...创建 Runnables 以发布到您的帖子...
private Runnable startMP = new Runnable(){
public void run(){
activeMP.start();
SongLooperHandler.postDelayed(timedQueue,activeSongMilliseconds);
}
};
private Runnable timedQueue = new Runnable(){
public void run(){
swapActivePlayers();
}
};
在您的服务的onStartCommand()或您的Activity中的某个位置,启动MediaPlayer ...
...
SongLooperHandler.post(startMP);
...
答案 6 :(得分:0)
我已经尝试了这里和其他地方建议的所有内容,唯一起作用的是ExoPlayer而不是Music类。您可以使用以下命令访问libgdx文件:
Uri.parse("file:///android_asset/" + path)
答案 7 :(得分:0)
CODE-REad的LoopMediaPlayer示例很好,但是如果您使用创建MediaPlayer的新MediaPlayer()方法(就像我为使用File或AssetFileDescriptor数据源所做的那样)而不是MediaPlayer.Create()方法,则必须小心
我已经修改了他的代码,以使用新的MediaPlayer()方法创建播放器,并且还添加了从AssetFileDescriptor和File设置数据源的功能。我希望这可以节省一些时间。
public class LoopMediaPlayer {
private static final String TAG = LoopMediaPlayer.class.getSimpleName();
private Context mContext = null;
private int mResId = 0;
private int mCounter = 1;
private AssetFileDescriptor mAfd = null;
private File mFile = null;
private MediaPlayer mCurrentPlayer = null;
private MediaPlayer mNextPlayer = null;
public static LoopMediaPlayer create(Context context, int resId) {
return new LoopMediaPlayer(context, resId);
}
public LoopMediaPlayer(Context context, File file){
mContext = context;
mFile = file;
try {
mCurrentPlayer = new MediaPlayer();
mCurrentPlayer.setLooping(false);
mCurrentPlayer.setDataSource(file.getAbsolutePath());
mCurrentPlayer.prepareAsync();
mCurrentPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
mCurrentPlayer.start();
mCurrentPlayer.setOnCompletionListener(onCompletionListener);
createNextMediaPlayer();
}
});
} catch (Exception e) {
Log.e("media", e.getLocalizedMessage());
}
}
public LoopMediaPlayer(Context context, AssetFileDescriptor afd){
mAfd = afd;
mContext = context;
try {
mCurrentPlayer = new MediaPlayer();
mCurrentPlayer.setLooping(false);
mCurrentPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
mCurrentPlayer.prepareAsync();
mCurrentPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
mCurrentPlayer.start();
mCurrentPlayer.setOnCompletionListener(onCompletionListener);
createNextMediaPlayer();
}
});
} catch (Exception e) {
Log.e("media", e.getLocalizedMessage());
}
}
private LoopMediaPlayer(Context context, int resId) {
mContext = context;
mResId = resId;
mCurrentPlayer = MediaPlayer.create(mContext, mResId);
mCurrentPlayer.setLooping(false);
mCurrentPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
mCurrentPlayer.start();
mCurrentPlayer.setOnCompletionListener(onCompletionListener);
createNextMediaPlayer();
}
});
mCurrentPlayer.prepareAsync();
}
private void createNextMediaPlayer() {
try{
if(mAfd != null){
mNextPlayer = new MediaPlayer();
mNextPlayer.setDataSource(mAfd.getFileDescriptor(), mAfd.getStartOffset(), mAfd.getLength());
mNextPlayer.prepareAsync();
mNextPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mCurrentPlayer.setNextMediaPlayer(mNextPlayer);
}
});
}
else if(mFile!=null){
mNextPlayer = new MediaPlayer();
mNextPlayer.setDataSource(mFile.getAbsolutePath());
mNextPlayer.prepareAsync();
mNextPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mCurrentPlayer.setNextMediaPlayer(mNextPlayer);
}
});
}
else {
mNextPlayer = MediaPlayer.create(mContext, mResId);
mNextPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mCurrentPlayer.setNextMediaPlayer(mNextPlayer);
}
});
}
} catch (Exception e) {
}
}
private final MediaPlayer.OnCompletionListener onCompletionListener = new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.release();
mCurrentPlayer = mNextPlayer;
mCurrentPlayer.setOnCompletionListener(onCompletionListener);
createNextMediaPlayer();
Log.d("LoopMediaPlayer", String.format("Loop #%d", ++mCounter));
}
};
// code-read additions:
public boolean isPlaying() throws IllegalStateException {
return mCurrentPlayer.isPlaying();
}
public void setVolume(float leftVolume, float rightVolume) {
mCurrentPlayer.setVolume(leftVolume, rightVolume);
}
public void start() throws IllegalStateException {
mCurrentPlayer.start();
}
public void stop() throws IllegalStateException {
mCurrentPlayer.stop();
}
public void pause() throws IllegalStateException {
mCurrentPlayer.pause();
}
public void release() {
mCurrentPlayer.release();
mNextPlayer.release();
}
public void reset() {
mCurrentPlayer.reset();
}
}
答案 8 :(得分:0)
使用Mattia Maestrini's answer时,我能够按照自己想要的方式进行音频循环播放,但是,由于我将其用于Android Auto,因此发现音频只能通过手机扬声器而不是汽车扬声器播放。最终,我发现this answer指出了一个错误,该错误使得在这种情况下将new MediaPlayer()
构造函数与setDataSource
方法一起使用很重要。我已经在代码中使用Uri
了,所以我使用了该变体,因此我不确定100%的重要性,我假设其他任何setDataSource
变体就足够了对您的代码很重要。
这是最终为我工作的东西:
public class LoopMediaPlayer extends MediaPlayer {
private static final String TAG = LoopMediaPlayer.class.getSimpleName();
private Context mContext = null;
private Uri mMediaUri = null;
private int mCounter = 1;
private MediaPlayer mCurrentPlayer = null;
private MediaPlayer mNextPlayer = null;
private Float mLeftVolume;
private Float mRightVolume;
public static LoopMediaPlayer create(Context context, Uri mediaUri) {
try {
return new LoopMediaPlayer(context, mediaUri);
}
catch (Exception e) {
throw new RuntimeException("Unable to create media player", e);
}
}
private LoopMediaPlayer(Context context, Uri mediaUri) throws IOException {
mContext = context;
mMediaUri = mediaUri;
mCurrentPlayer = new MediaPlayer();
mCurrentPlayer.setDataSource(mContext, mMediaUri);
mCurrentPlayer.prepare();
createNextMediaPlayer();
}
private void createNextMediaPlayer() {
try {
mNextPlayer = new MediaPlayer();
mNextPlayer.setDataSource(mContext, mMediaUri);
if (mLeftVolume != null && mRightVolume != null) {
mNextPlayer.setVolume(mLeftVolume, mRightVolume);
}
mNextPlayer.prepare();
mCurrentPlayer.setNextMediaPlayer(mNextPlayer);
mCurrentPlayer.setOnCompletionListener(onCompletionListener);
}
catch (Exception e) {
Log.e(TAG, "Problem creating next media player", e);
}
}
private MediaPlayer.OnCompletionListener onCompletionListener = new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.release();
mCurrentPlayer = mNextPlayer;
createNextMediaPlayer();
Log.d(TAG, String.format("Loop #%d", ++mCounter));
}
};
@Override
public void prepare() throws IllegalStateException {
// no-op, internal media-players are prepared when they are created.
}
@Override
public boolean isPlaying() throws IllegalStateException {
return mCurrentPlayer.isPlaying();
}
@Override
public void setVolume(float leftVolume, float rightVolume) {
mCurrentPlayer.setVolume(leftVolume, rightVolume);
mNextPlayer.setVolume(leftVolume, rightVolume);
mLeftVolume = leftVolume;
mRightVolume = rightVolume;
}
@Override
public void start() throws IllegalStateException {
mCurrentPlayer.start();
}
@Override
public void stop() throws IllegalStateException {
mCurrentPlayer.stop();
}
@Override
public void pause() throws IllegalStateException {
mCurrentPlayer.pause();
}
@Override
public void release() {
mCurrentPlayer.release();
mNextPlayer.release();
}
@Override
public void reset() {
mCurrentPlayer.reset();
}
}