修改
Android 2.2 MediaPlayer is working fine with one SHOUTcast URL but not with the other one
我需要播放外部网址(shoutcast stream)中的音频文件。目前,音频文件以递增方式下载。一旦我们在手机本地临时存储中获得足够的音频,就会播放。我正在使用StreamingMediaPlayer class。
检查这段代码:
private MediaPlayer createMediaPlayer(File mediaFile)
throws IOException {
MediaPlayer mPlayer = new MediaPlayer();
//example of mediaFile =/data/data/package/cache/playingMedia0.dat
FileInputStream fis = new FileInputStream(mediaFile);
mPlayer.setDataSource(fis.getFD());
mPlayer.prepare();
return mPlayer;
}
当前状态:
1-从Android 1.6到2.1可以正常工作,但在Android 2.2等较高版本中则不行。
2-“mPlayer.setDataSource(fis.getFD())”是抛出错误的行。
3-错误是“无法创建媒体播放器”
尝试了其他解决方案:
我尝试了下面的替代解决方案,但到目前为止没有任何工作。
Android 2.2 MediaPlayer is working fine with one SHOUTcast URL but not with the other one
我在寻找什么?
我的目标是拥有适用于Android 2.1和Android的代码。更高。
此问题也在此处讨论:
1- Inconsistent 2.2 Media Player Behavior
2- android code for streaming shoutcast stream breaks in 2.2
3-本期网站上的很多问题也讨论了这个问题,但我发现答案没有。
4- markmail.org
LogCat跟踪:
Unable to to create media player
Error copying buffered conent.
java.lang.NullPointerException
com.ms.iradio.StreamingMediaPlayer.startMediaPlayer(StreamingMediaPlayer.java:251)
com.ms.iradio.StreamingMediaPlayer.access$2(StreamingMediaPlayer.java:221)
com.ms.iradio.StreamingMediaPlayer$2.run(StreamingMediaPlayer.java:204)
android.os.Handler.handleCallback(Handler.java:587)
android.os.Handler.dispatchMessage(Handler.java:92)
android.os.Looper.loop(Looper.java:123)
android.app.ActivityThread.main(ActivityThread.java:3683)
java.lang.reflect.Method.invokeNative(Native Method)
java.lang.reflect.Method.invoke(Method.java:507)
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
dalvik.system.NativeStart.main(Native Method)
答案 0 :(得分:5)
问题是不直接支持内容类型“audio / aacp”流式传输。一些解码库可用于播放“aacp”,请参阅下面的解决方案:
Freeware Advanced Audio (AAC) Decoder for Android
<强> Consider legal issues while using it 强>
[T]他的项目http://code.google.com/p/aacplayer-android/已获得许可 根据GPL,您可以在其上创建商业应用程序,但是您 需要填写GPL - 主要是指将代码发布为 好。如果您使用第二个项目 http://code.google.com/p/aacdecoder-android/,那么你不需要 发布您的代码(该库是根据LGPL许可的。)
答案 1 :(得分:3)
StreamingMediaPlayer
类正在使用双缓冲技术来克服Android 1.2之前版本中的限制。 Android OS的所有生产版本都包含支持流媒体的MediaPlayer(1)。我建议这样做,而不是使用这种双缓冲技术来解决问题。
Android OS 2.2用FrightCast播放器替换旧的媒体播放器代码,在这种情况下可能采用不同的行为。
堆栈跟踪中的行号不会映射到您链接的文件,因此我假设您实际使用的版本不同。我猜测NullPointerException
正在报告MediaPlayer
,但FileInputStream
和返回的FileDescriptor
都不能null
。
(1)在版本2.2之前,媒体播放器无法识别响应中带有“ICY / 1.1”版本标头的ShoutCast流。通过创建用“HTTP / 1.1”替换它的代理,您可以解决这个问题。有关示例,请参阅StreamProxy class here。
答案 2 :(得分:1)
我正在使用此代码并运行2.2到更高版本的流媒体下载。
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import android.content.Context;
import android.media.MediaPlayer;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.TextView;
public class StreamingMediaPlayer {
private static final int INTIAL_KB_BUFFER = 96*10;//assume 96kbps*10secs/8bits per byte
private TextView textStreamed;
private ImageButton playButton;
private ProgressBar progressBar;
ProgressBar pb;
int audiofiletime=0;
private long mediaLengthInSeconds;
private int totalKbRead = 0;
int totalsize=0;
int numread;
int totalBytesRead = 0;
private final Handler handler = new Handler();
private MediaPlayer mediaPlayer;
private File downloadingMediaFile;
private boolean isInterrupted;
private Context context;
private int counter = 0;
public StreamingMediaPlayer(Context context,TextView textStreamed, ImageButton playButton, Button streamButton,ProgressBar progressBar,ProgressBar pb)
{
this.context = context;
this.textStreamed = textStreamed;
this.playButton = playButton;
this.progressBar = progressBar;
this.pb=pb;
}
/**
* Progressivly download the media to a temporary location and update the MediaPlayer as new content becomes available.
*/
public void startStreaming(final String mediaUrl) throws IOException {
//this.mediaLengthInSeconds = 100;
Runnable r = new Runnable() {
public void run() {
try {
downloadAudioIncrement(mediaUrl);
} catch (IOException e) {
Log.e(getClass().getName(), "Unable to initialize the MediaPlayer for fileUrl=" + mediaUrl, e);
return;
}
}
};
new Thread(r).start();
}
/**
* Download the url stream to a temporary location and then call the setDataSource
* for that local file
*/
@SuppressWarnings({ "resource", "unused" })
public void downloadAudioIncrement(String mediaUrl) throws IOException {
URLConnection cn = new URL(mediaUrl).openConnection();
cn.connect();
InputStream stream = cn.getInputStream();
if (stream == null) {
Log.e(getClass().getName(), "Unable to create InputStream for mediaUrl:" + mediaUrl);
}
///////////////////save sdcard///////////////
File direct = new File(Environment.getExternalStorageDirectory()+"/punya");
if(!direct.exists()) {
if(direct.mkdir()); //directory is created;
}
String[] files=mediaUrl.split("/");
String fileName=files[files.length-1];
fileName = fileName.replace(".m4a", ".rdo");
//create a new file, to save the downloaded file
File file = new File(direct,fileName);
@SuppressWarnings("resource")
FileOutputStream fileOutput = new FileOutputStream(file);
///////////////////end/////////////////
totalsize=cn.getContentLength();
//mediaLengthInKb = 10000;
downloadingMediaFile = new File(context.getCacheDir(),fileName);
if (downloadingMediaFile.exists()) {
downloadingMediaFile.delete();
}
FileOutputStream out = new FileOutputStream(downloadingMediaFile);
byte buf[] = new byte[16384];
int incrementalBytesRead = 0;
do {
numread = stream.read(buf);
if (numread <= 0)
break;
out.write(buf, 0, numread);
fileOutput.write(buf, 0, numread);
totalBytesRead += numread;
incrementalBytesRead += numread;
totalKbRead = totalBytesRead/1000;
// pb.setMax(100);
// pb.setProgress(totalKbRead);
testMediaBuffer();
fireDataLoadUpdate();
} while (validateNotInterrupted());
stream.close();
if (validateNotInterrupted()) {
fireDataFullyLoaded();
}
}
private boolean validateNotInterrupted() {
if (isInterrupted) {
if (mediaPlayer != null) {
mediaPlayer.pause();
//mediaPlayer.release();
}
return false;
} else {
return true;
}
}
/**
* Test whether we need to transfer buffered data to the MediaPlayer.
* Interacting with MediaPlayer on non-main UI thread can causes crashes to so perform this using a Handler.
*/
private void testMediaBuffer() {
Runnable updater = new Runnable() {
public void run() {
if (mediaPlayer == null) {
// Only create the MediaPlayer once we have the minimum buffered data
if ( totalKbRead >= INTIAL_KB_BUFFER) {
try {
startMediaPlayer();
} catch (Exception e) {
Log.e(getClass().getName(), "Error copying buffered conent.", e);
}
}
} else if ( mediaPlayer.getDuration() - mediaPlayer.getCurrentPosition() <= 1000 ){
// NOTE: The media player has stopped at the end so transfer any existing buffered data
// We test for < 1second of data because the media player can stop when there is still
// a few milliseconds of data left to play
transferBufferToMediaPlayer();
}
}
};
handler.post(updater);
}
private void startMediaPlayer() {
try {
//File bufferedFile = new File(context.getCacheDir(),"playingMedia" + (counter++) + ".m4a");
//moveFile(downloadingMediaFile,bufferedFile);
// Log.e(getClass().getName(),"Buffered File path: " + bufferedFile.getAbsolutePath());
// Log.e(getClass().getName(),"Buffered File length: " + bufferedFile.length()+"");
mediaPlayer = createMediaPlayer(downloadingMediaFile);
//mediaPlayer.start();
startPlayProgressUpdater();
//playButton.setEnabled(true);
playButton.setVisibility(View.VISIBLE);
} catch (IOException e) {
Log.e(getClass().getName(), "Error initializing the MediaPlayer.", e);
return;
}
}
private MediaPlayer createMediaPlayer(File mediaFile)
throws IOException {
MediaPlayer mPlayer = new MediaPlayer();
mPlayer.setOnErrorListener(
new MediaPlayer.OnErrorListener() {
public boolean onError(MediaPlayer mp, int what, int extra) {
Log.e(getClass().getName(), "Error in MediaPlayer: (" + what +") with extra (" +extra +")" );
return false;
}
});
FileInputStream fis = new FileInputStream(mediaFile);
mPlayer.setDataSource(fis.getFD());
mPlayer.prepare();
return mPlayer;
}
/**
* Transfer buffered data to the MediaPlayer.
* NOTE: Interacting with a MediaPlayer on a non-main UI thread can cause thread-lock and crashes so
* this method should always be called using a Handler.
*/
private void transferBufferToMediaPlayer() {
try {
boolean wasPlaying = mediaPlayer.isPlaying();
int curPosition = mediaPlayer.getCurrentPosition();
File oldBufferedFile = new File(context.getCacheDir(),"playingMedia" + counter + ".m4a");
File bufferedFile = new File(context.getCacheDir(),"playingMedia" + (counter++) + ".m4a");
bufferedFile.deleteOnExit();
moveFile(downloadingMediaFile,bufferedFile);
//mediaPlayer.pause();
mediaPlayer.release();
mediaPlayer = createMediaPlayer(bufferedFile);
mediaPlayer.seekTo(curPosition);
boolean atEndOfFile = mediaPlayer.getDuration() - mediaPlayer.getCurrentPosition() <= 1000;
if (wasPlaying || atEndOfFile){
mediaPlayer.start();
}
oldBufferedFile.delete();
}catch (Exception e) {
Log.e(getClass().getName(), "Error updating to newly loaded content.", e);
}
}
private void fireDataLoadUpdate() {
Runnable updater = new Runnable() {
public void run() {
//float loadProgress = ((float)totalBytesRead/(float)mediaLengthInKb);
//float per = ((float)numread/mediaLengthInKb) * 100;
float per = ((float)totalBytesRead/totalsize) * 100;
textStreamed.setText((totalKbRead + " Kb (" + (int)per + "%)"));
progressBar.setSecondaryProgress((int)(per));
pb.setSecondaryProgress((int)(per));
}
};
handler.post(updater);
}
private void fireDataFullyLoaded() {
Runnable updater = new Runnable() {
public void run() {
transferBufferToMediaPlayer();
downloadingMediaFile.delete();
textStreamed.setText(("Download completed" ));
}
};
handler.post(updater);
}
public MediaPlayer getMediaPlayer() {
return mediaPlayer;
}
public void startPlayProgressUpdater() {
audiofiletime =mediaPlayer.getDuration();
float progress = (((float)mediaPlayer.getCurrentPosition()/ audiofiletime) * 100);
progressBar.setProgress((int)(progress));
//pb.setProgress((int)(progress*100));
if (mediaPlayer.isPlaying()) {
Runnable notification = new Runnable() {
public void run() {
startPlayProgressUpdater();
}
};
handler.postDelayed(notification,1000);
}
}
public void interrupt() {
playButton.setEnabled(false);
isInterrupted = true;
validateNotInterrupted();
}
/**
* Move the file in oldLocation to newLocation.
*/
public void moveFile(File oldLocation, File newLocation)
throws IOException {
if ( oldLocation.exists( )) {
BufferedInputStream reader = new BufferedInputStream( new FileInputStream(oldLocation) );
BufferedOutputStream writer = new BufferedOutputStream( new FileOutputStream(newLocation, false));
try {
byte[] buff = new byte[5461];
int numChars;
while ( (numChars = reader.read( buff, 0, buff.length ) ) != -1) {
writer.write( buff, 0, numChars );
}
} catch( IOException ex ) {
throw new IOException("IOException when transferring " + oldLocation.getPath() + " to " + newLocation.getPath());
} finally {
try {
if ( reader != null ){
writer.close();
reader.close();
}
} catch( IOException ex ){
Log.e(getClass().getName(),"Error closing files when transferring " + oldLocation.getPath() + " to " + newLocation.getPath() );
}
}
} else {
throw new IOException("Old location does not exist when transferring " + oldLocation.getPath() + " to " + newLocation.getPath() );
}
}
}