用于流式shoutcast流的android代码在2.2中中断
以下代码在 Android 2.1update1 上运行良好 -
package com.troubadorian.android.teststreaming;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.Gallery;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.Gallery.LayoutParams;
import android.widget.ImageSwitcher;
import android.widget.ImageView;
import android.widget.ViewSwitcher;
public class TestStreaming extends Activity
{
private Button streamButton;
private ImageButton playButton;
private TextView textStreamed;
private boolean isPlaying;
private StreamingMediaPlayer audioStreamer;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main);
initControls();
}
protected void onDestroy()
{
super.onDestroy();
Toast.makeText(TestStreaming.this, "...exiting application..." ,Toast.LENGTH_SHORT).show();
if ( audioStreamer != null)
{
audioStreamer.interrupt();
}
}
private void initControls()
{
textStreamed = (TextView) findViewById(R.id.text_kb_streamed);
streamButton = (Button) findViewById(R.id.button_stream);
streamButton.setOnClickListener(new View.OnClickListener()
{
public void onClick(View view)
{
String urlstring2 = "url to a shoutcase stream";
Toast
.makeText(
TestStreaming.this,
"The following stream is about to start" + urlstring2,
Toast.LENGTH_LONG).show();
startStreamingAudio(urlstring2);
}
});
playButton = (ImageButton) findViewById(R.id.button_play);
playButton.setEnabled(false);
playButton.setOnClickListener(new View.OnClickListener()
{
public void onClick(View view)
{
if (audioStreamer.getMediaPlayer().isPlaying())
{
audioStreamer.getMediaPlayer().pause();
playButton.setImageResource(R.drawable.button_play);
} else
{
audioStreamer.getMediaPlayer().start();
audioStreamer.startPlayProgressUpdater();
playButton.setImageResource(R.drawable.button_pause);
}
isPlaying = !isPlaying;
}
});
}
private void startStreamingAudio(String urlstring) {
try {
final ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress_bar);
if ( audioStreamer != null) {
audioStreamer.interrupt();
}
audioStreamer = new StreamingMediaPlayer(this,textStreamed, playButton, streamButton,progressBar);
audioStreamer.startStreaming(urlstring,5208, 216);
streamButton.setEnabled(false);
} catch (Exception e)
{
Log.e(getClass().getName(), "Error starting to stream audio.", e);
}
}
public void onItemSelected(AdapterView parent, View v, int position, long id)
{
mSwitcher.setImageResource(mImageIds[position]);
}
public void onNothingSelected(AdapterView parent)
{
}
public View makeView()
{
ImageView i = new ImageView(this);
i.setBackgroundColor(0xFF000000);
i.setScaleType(ImageView.ScaleType.FIT_CENTER);
i.setLayoutParams(new ImageSwitcher.LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
return i;
}
private ImageSwitcher mSwitcher;
public class ImageAdapter extends BaseAdapter
{
public ImageAdapter(Context c)
{
mContext = c;
}
public int getCount()
{
return mThumbIds.length;
}
public Object getItem(int position)
{
return position;
}
public long getItemId(int position)
{
return position;
}
public View getView(int position, View convertView, ViewGroup parent)
{
ImageView i = new ImageView(mContext);
i.setImageResource(mThumbIds[position]);
i.setAdjustViewBounds(true);
i.setLayoutParams(new Gallery.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
i.setBackgroundResource(R.drawable.picture_frame);
return i;
}
private Context mContext;
}
private Integer[] mThumbIds =
{ R.drawable.calculator, R.drawable.calendar, R.drawable.camera };
private Integer[] mImageIds =
{ R.drawable.calculator, R.drawable.calendar, R.drawable.camera };
}
StreamingMediaPlayer 类如下 -
package com.troubadorian.android.teststreaming;
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.AudioManager;
import android.media.MediaPlayer;
import android.os.Handler;
import android.util.Log;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.TextView;
/**
* MediaPlayer does not yet support streaming from external URLs so this class
* provides a pseudo-streaming function by downloading the content incrementally
* & playing as soon as we get enough audio in our temporary storage.
*/
public class StreamingMediaPlayer
{
private static final int INTIAL_KB_BUFFER = 96 * 10 / 8;// assume
// 96kbps*10secs/8bits
// per byte
private TextView textStreamed;
private ImageButton playButton;
private ProgressBar progressBar;
// Track for display by progressBar
private long mediaLengthInKb, mediaLengthInSeconds;
private int totalKbRead = 0;
// Create Handler to call View updates on the main UI thread.
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)
{
this.context = context;
this.textStreamed = textStreamed;
this.playButton = playButton;
this.progressBar = progressBar;
}
/**
* Progressivly download the media to a temporary location and update the
* MediaPlayer as new content becomes available.
*/
public void startStreaming(final String mediaUrl, long mediaLengthInKb,
long mediaLengthInSeconds) throws IOException
{
this.mediaLengthInKb = mediaLengthInKb;
this.mediaLengthInSeconds = mediaLengthInSeconds;
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
*/
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);
}
downloadingMediaFile = new File(context.getCacheDir(),
"downloadingMedia.dat");
// Just in case a prior deletion failed because our code crashed or
// something, we also delete any previously
// downloaded file to ensure we start fresh. If you use this code,
// always delete
// no longer used downloads else you'll quickly fill up your hard disk
// memory. Of course, you can also
// store any previously downloaded file in a separate data cache for
// instant replay if you wanted as well.
if (downloadingMediaFile.exists())
{
downloadingMediaFile.delete();
}
FileOutputStream out = new FileOutputStream(downloadingMediaFile);
byte buf[] = new byte[16384];
int totalBytesRead = 0, incrementalBytesRead = 0;
do
{
int numread = stream.read(buf);
if (numread <= 0)
break;
out.write(buf, 0, numread);
totalBytesRead += numread;
incrementalBytesRead += numread;
totalKbRead = totalBytesRead / 1000;
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++) + ".dat");
// We double buffer the data to avoid potential read/write errors
// that could happen if the
// download thread attempted to write at the same time the
// MediaPlayer was trying to read.
// For example, we can't guarantee that the MediaPlayer won't open a
// file for playing and leave it locked while
// the media is playing. This would permanently deadlock the file
// download. To avoid such a deadloack,
// we move the currently loaded data to a temporary buffer file that
// we start playing while the remaining
// data downloads.
moveFile(downloadingMediaFile, bufferedFile);
Log.e(getClass().getName(),
"Buffered File path: " + bufferedFile.getAbsolutePath());
Log.e(getClass().getName(),
"Buffered File length: " + bufferedFile.length() + "");
mediaPlayer = createMediaPlayer(bufferedFile);
// We have pre-loaded enough content and started the MediaPlayer so
// update the buttons & progress meters.
mediaPlayer.start();
startPlayProgressUpdater();
playButton.setEnabled(true);
} 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;
}
});
// It appears that for security/permission reasons, it is better to pass
// a FileDescriptor rather than a direct path to the File.
// Also I have seen errors such as "PVMFErrNotSupported" and
// "Prepare failed.: status=0x1" if a file path String is passed to
// setDataSource(). So unless otherwise noted, we use a FileDescriptor
// here.
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
{
// First determine if we need to restart the player after
// transferring data...e.g. perhaps the user pressed pause
boolean wasPlaying = mediaPlayer.isPlaying();
int curPosition = mediaPlayer.getCurrentPosition();
// Copy the currently downloaded content to a new buffered File.
// Store the old File for deleting later.
File oldBufferedFile = new File(context.getCacheDir(),
"playingMedia" + counter + ".dat");
File bufferedFile = new File(context.getCacheDir(), "playingMedia"
+ (counter++) + ".dat");
// This may be the last buffered File so ask that it be delete on
// exit. If it's already deleted, then this won't mean anything. If
// you want to
// keep and track fully downloaded files for later use, write
// caching code and please send me a copy.
bufferedFile.deleteOnExit();
moveFile(downloadingMediaFile, bufferedFile);
// Pause the current player now as we are about to create and start
// a new one. So far (Android v1.5),
// this always happens so quickly that the user never realized we've
// stopped the player and started a new one
mediaPlayer.pause();
// Create a new MediaPlayer rather than try to re-prepare the prior
// one.
mediaPlayer = createMediaPlayer(bufferedFile);
mediaPlayer.seekTo(curPosition);
// Restart if at end of prior buffered content or mediaPlayer was
// previously playing.
// NOTE: We test for < 1second of data because the media player can
// stop when there is still
// a few milliseconds of data left to play
boolean atEndOfFile = mediaPlayer.getDuration()
- mediaPlayer.getCurrentPosition() <= 1000;
if (wasPlaying || atEndOfFile)
{
mediaPlayer.start();
}
// Lastly delete the previously playing buffered File as it's no
// longer needed.
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()
{
textStreamed.setText((totalKbRead + " Kb read"));
float loadProgress = ((float) totalKbRead / (float) mediaLengthInKb);
progressBar.setSecondaryProgress((int) (loadProgress * 100));
}
};
handler.post(updater);
}
private void fireDataFullyLoaded()
{
Runnable updater = new Runnable()
{
public void run()
{
transferBufferToMediaPlayer();
// Delete the downloaded File as it's now been transferred to
// the currently playing buffer file.
downloadingMediaFile.delete();
textStreamed
.setText(("Audio full loaded: " + totalKbRead + " Kb read"));
}
};
handler.post(updater);
}
public MediaPlayer getMediaPlayer()
{
return mediaPlayer;
}
public void startPlayProgressUpdater()
{
float progress = (((float) mediaPlayer.getCurrentPosition() / 1000) / mediaLengthInSeconds);
progressBar.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[8192];
/* changing the size of the buffer */
byte[] buff = new byte[16384];
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());
}
}
}
布局文件如下 -
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:id="@+id/Logo"
android:orientation="vertical">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10px">
<TextView android:id="@+id/text_kb_streamed"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textStyle="bold"
android:text="...streaming audio..."/>
<Button android:id="@+id/button_stream"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10px"
style="?android:attr/buttonStyleSmall"
android:text="Start Streaming"/>
<ProgressBar android:id="@+id/progress_bar"
android:layout_width="200px"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
style="?android:attr/progressBarStyleHorizontal"/>
<ImageButton android:id="@+id/button_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5px"
style="?android:attr/buttonStyleSmall"
android:src="@drawable/button_pause"/>
</LinearLayout>
</LinearLayout>
<ViewFlipper
android:id="@+id/MainFlipper"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="@+id/Logo"
android:layout_marginBottom="37dip">
</ViewFlipper>
<FrameLayout
android:id="@+id/MediaPlayer"
android:layout_width="fill_parent"
android:layout_height="130dip"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true">
</FrameLayout>
</RelativeLayout>
但相同的代码在 Android 2.2 中不起作用。可以修复它在 2.2 上运行吗?
这是我收到的错误 -
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): Error initializing the MediaPlayer.
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): java.io.IOException: setDataSourceFD failed.: status=0x80000000
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at android.media.MediaPlayer.setDataSource(Native Method)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at android.media.MediaPlayer.setDataSource(MediaPlayer.java:749)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at com.troubadorian.android.teststreaming.StreamingMediaPlayer.createMediaPlayer(StreamingMediaPlayer.java:272)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at com.troubadorian.android.teststreaming.StreamingMediaPlayer.startMediaPlayer(StreamingMediaPlayer.java:237)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at com.troubadorian.android.teststreaming.StreamingMediaPlayer.access$2(StreamingMediaPlayer.java:212)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at com.troubadorian.android.teststreaming.StreamingMediaPlayer$2.run(StreamingMediaPlayer.java:190)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at android.os.Handler.handleCallback(Handler.java:587)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at android.os.Handler.dispatchMessage(Handler.java:92)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at android.os.Looper.loop(Looper.java:123)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at android.app.ActivityThread.main(ActivityThread.java:4627)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at java.lang.reflect.Method.invokeNative(Native Method)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at java.lang.reflect.Method.invoke(Method.java:521)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at dalvik.system.NativeStart.main(Native Method)
I/global ( 1423): Default buffer size used in BufferedInputStream constructor. It would be better to be explicit if an 8k buffer is required.
I/global ( 1423): Default buffer size used in BufferedOutputStream constructor. It would be better to be explicit if an 8k buffer is required.
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): Buffered File path: /data/data/com.troubadorian.android.teststreaming/cache/playingMedia34.dat
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): Buffered File length: 193188
E/MediaPlayer( 1423): Unable to to create media player
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): Error initializing the MediaPlayer.
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): java.io.IOException: setDataSourceFD failed.: status=0x80000000
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at android.media.MediaPlayer.setDataSource(Native Method)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at android.media.MediaPlayer.setDataSource(MediaPlayer.java:749)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at com.troubadorian.android.teststreaming.StreamingMediaPlayer.createMediaPlayer(StreamingMediaPlayer.java:272)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at com.troubadorian.android.teststreaming.StreamingMediaPlayer.startMediaPlayer(StreamingMediaPlayer.java:237)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at com.troubadorian.android.teststreaming.StreamingMediaPlayer.access$2(StreamingMediaPlayer.java:212)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at com.troubadorian.android.teststreaming.StreamingMediaPlayer$2.run(StreamingMediaPlayer.java:190)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at android.os.Handler.handleCallback(Handler.java:587)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at android.os.Handler.dispatchMessage(Handler.java:92)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at android.os.Looper.loop(Looper.java:123)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at android.app.ActivityThread.main(ActivityThread.java:4627)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at java.lang.reflect.Method.invokeNative(Native Method)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at java.lang.reflect.Method.invoke(Method.java:521)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at dalvik.system.NativeStart.main(Native Method)
I/global ( 1423): Default buffer size used in BufferedInputStream constructor. It would be better to be explicit if an 8k buffer is required.
I/global ( 1423): Default buffer size used in BufferedOutputStream constructor. It would be better to be explicit if an 8k buffer is required.
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): Buffered File path: /data/data/com.troubadorian.android.teststreaming/cache/playingMedia35.dat
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): Buffered File length: 194588
E/MediaPlayer( 1423): Unable to to create media player
请帮助。
The following code works fine on Android 2.1update1 -
package com.troubadorian.android.teststreaming;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.Gallery;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.Gallery.LayoutParams;
import android.widget.ImageSwitcher;
import android.widget.ImageView;
import android.widget.ViewSwitcher;
public class TestStreaming extends Activity
{
private Button streamButton;
private ImageButton playButton;
private TextView textStreamed;
private boolean isPlaying;
private StreamingMediaPlayer audioStreamer;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main);
initControls();
}
protected void onDestroy()
{
super.onDestroy();
Toast.makeText(TestStreaming.this, "...exiting application..." ,Toast.LENGTH_SHORT).show();
if ( audioStreamer != null)
{
audioStreamer.interrupt();
}
}
private void initControls()
{
textStreamed = (TextView) findViewById(R.id.text_kb_streamed);
streamButton = (Button) findViewById(R.id.button_stream);
streamButton.setOnClickListener(new View.OnClickListener()
{
public void onClick(View view)
{
String urlstring2 = "url to a shoutcase stream";
Toast
.makeText(
TestStreaming.this,
"The following stream is about to start" + urlstring2,
Toast.LENGTH_LONG).show();
startStreamingAudio(urlstring2);
}
});
playButton = (ImageButton) findViewById(R.id.button_play);
playButton.setEnabled(false);
playButton.setOnClickListener(new View.OnClickListener()
{
public void onClick(View view)
{
if (audioStreamer.getMediaPlayer().isPlaying())
{
audioStreamer.getMediaPlayer().pause();
playButton.setImageResource(R.drawable.button_play);
} else
{
audioStreamer.getMediaPlayer().start();
audioStreamer.startPlayProgressUpdater();
playButton.setImageResource(R.drawable.button_pause);
}
isPlaying = !isPlaying;
}
});
}
private void startStreamingAudio(String urlstring) {
try {
final ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress_bar);
if ( audioStreamer != null) {
audioStreamer.interrupt();
}
audioStreamer = new StreamingMediaPlayer(this,textStreamed, playButton, streamButton,progressBar);
audioStreamer.startStreaming(urlstring,5208, 216);
streamButton.setEnabled(false);
} catch (Exception e)
{
Log.e(getClass().getName(), "Error starting to stream audio.", e);
}
}
public void onItemSelected(AdapterView parent, View v, int position, long id)
{
mSwitcher.setImageResource(mImageIds[position]);
}
public void onNothingSelected(AdapterView parent)
{
}
public View makeView()
{
ImageView i = new ImageView(this);
i.setBackgroundColor(0xFF000000);
i.setScaleType(ImageView.ScaleType.FIT_CENTER);
i.setLayoutParams(new ImageSwitcher.LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
return i;
}
private ImageSwitcher mSwitcher;
public class ImageAdapter extends BaseAdapter
{
public ImageAdapter(Context c)
{
mContext = c;
}
public int getCount()
{
return mThumbIds.length;
}
public Object getItem(int position)
{
return position;
}
public long getItemId(int position)
{
return position;
}
public View getView(int position, View convertView, ViewGroup parent)
{
ImageView i = new ImageView(mContext);
i.setImageResource(mThumbIds[position]);
i.setAdjustViewBounds(true);
i.setLayoutParams(new Gallery.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
i.setBackgroundResource(R.drawable.picture_frame);
return i;
}
private Context mContext;
}
private Integer[] mThumbIds =
{ R.drawable.calculator, R.drawable.calendar, R.drawable.camera };
private Integer[] mImageIds =
{ R.drawable.calculator, R.drawable.calendar, R.drawable.camera };
}
with the StreamingMediaPlayer class as follows -
package com.troubadorian.android.teststreaming;
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.AudioManager;
import android.media.MediaPlayer;
import android.os.Handler;
import android.util.Log;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.TextView;
/**
* MediaPlayer does not yet support streaming from external URLs so this class
* provides a pseudo-streaming function by downloading the content incrementally
* & playing as soon as we get enough audio in our temporary storage.
*/
public class StreamingMediaPlayer
{
private static final int INTIAL_KB_BUFFER = 96 * 10 / 8;// assume
// 96kbps*10secs/8bits
// per byte
private TextView textStreamed;
private ImageButton playButton;
private ProgressBar progressBar;
// Track for display by progressBar
private long mediaLengthInKb, mediaLengthInSeconds;
private int totalKbRead = 0;
// Create Handler to call View updates on the main UI thread.
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)
{
this.context = context;
this.textStreamed = textStreamed;
this.playButton = playButton;
this.progressBar = progressBar;
}
/**
* Progressivly download the media to a temporary location and update the
* MediaPlayer as new content becomes available.
*/
public void startStreaming(final String mediaUrl, long mediaLengthInKb,
long mediaLengthInSeconds) throws IOException
{
this.mediaLengthInKb = mediaLengthInKb;
this.mediaLengthInSeconds = mediaLengthInSeconds;
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
*/
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);
}
downloadingMediaFile = new File(context.getCacheDir(),
"downloadingMedia.dat");
// Just in case a prior deletion failed because our code crashed or
// something, we also delete any previously
// downloaded file to ensure we start fresh. If you use this code,
// always delete
// no longer used downloads else you'll quickly fill up your hard disk
// memory. Of course, you can also
// store any previously downloaded file in a separate data cache for
// instant replay if you wanted as well.
if (downloadingMediaFile.exists())
{
downloadingMediaFile.delete();
}
FileOutputStream out = new FileOutputStream(downloadingMediaFile);
byte buf[] = new byte[16384];
int totalBytesRead = 0, incrementalBytesRead = 0;
do
{
int numread = stream.read(buf);
if (numread <= 0)
break;
out.write(buf, 0, numread);
totalBytesRead += numread;
incrementalBytesRead += numread;
totalKbRead = totalBytesRead / 1000;
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++) + ".dat");
// We double buffer the data to avoid potential read/write errors
// that could happen if the
// download thread attempted to write at the same time the
// MediaPlayer was trying to read.
// For example, we can't guarantee that the MediaPlayer won't open a
// file for playing and leave it locked while
// the media is playing. This would permanently deadlock the file
// download. To avoid such a deadloack,
// we move the currently loaded data to a temporary buffer file that
// we start playing while the remaining
// data downloads.
moveFile(downloadingMediaFile, bufferedFile);
Log.e(getClass().getName(),
"Buffered File path: " + bufferedFile.getAbsolutePath());
Log.e(getClass().getName(),
"Buffered File length: " + bufferedFile.length() + "");
mediaPlayer = createMediaPlayer(bufferedFile);
// We have pre-loaded enough content and started the MediaPlayer so
// update the buttons & progress meters.
mediaPlayer.start();
startPlayProgressUpdater();
playButton.setEnabled(true);
} 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;
}
});
// It appears that for security/permission reasons, it is better to pass
// a FileDescriptor rather than a direct path to the File.
// Also I have seen errors such as "PVMFErrNotSupported" and
// "Prepare failed.: status=0x1" if a file path String is passed to
// setDataSource(). So unless otherwise noted, we use a FileDescriptor
// here.
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
{
// First determine if we need to restart the player after
// transferring data...e.g. perhaps the user pressed pause
boolean wasPlaying = mediaPlayer.isPlaying();
int curPosition = mediaPlayer.getCurrentPosition();
// Copy the currently downloaded content to a new buffered File.
// Store the old File for deleting later.
File oldBufferedFile = new File(context.getCacheDir(),
"playingMedia" + counter + ".dat");
File bufferedFile = new File(context.getCacheDir(), "playingMedia"
+ (counter++) + ".dat");
// This may be the last buffered File so ask that it be delete on
// exit. If it's already deleted, then this won't mean anything. If
// you want to
// keep and track fully downloaded files for later use, write
// caching code and please send me a copy.
bufferedFile.deleteOnExit();
moveFile(downloadingMediaFile, bufferedFile);
// Pause the current player now as we are about to create and start
// a new one. So far (Android v1.5),
// this always happens so quickly that the user never realized we've
// stopped the player and started a new one
mediaPlayer.pause();
// Create a new MediaPlayer rather than try to re-prepare the prior
// one.
mediaPlayer = createMediaPlayer(bufferedFile);
mediaPlayer.seekTo(curPosition);
// Restart if at end of prior buffered content or mediaPlayer was
// previously playing.
// NOTE: We test for < 1second of data because the media player can
// stop when there is still
// a few milliseconds of data left to play
boolean atEndOfFile = mediaPlayer.getDuration()
- mediaPlayer.getCurrentPosition() <= 1000;
if (wasPlaying || atEndOfFile)
{
mediaPlayer.start();
}
// Lastly delete the previously playing buffered File as it's no
// longer needed.
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()
{
textStreamed.setText((totalKbRead + " Kb read"));
float loadProgress = ((float) totalKbRead / (float) mediaLengthInKb);
progressBar.setSecondaryProgress((int) (loadProgress * 100));
}
};
handler.post(updater);
}
private void fireDataFullyLoaded()
{
Runnable updater = new Runnable()
{
public void run()
{
transferBufferToMediaPlayer();
// Delete the downloaded File as it's now been transferred to
// the currently playing buffer file.
downloadingMediaFile.delete();
textStreamed
.setText(("Audio full loaded: " + totalKbRead + " Kb read"));
}
};
handler.post(updater);
}
public MediaPlayer getMediaPlayer()
{
return mediaPlayer;
}
public void startPlayProgressUpdater()
{
float progress = (((float) mediaPlayer.getCurrentPosition() / 1000) / mediaLengthInSeconds);
progressBar.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[8192];
/* changing the size of the buffer */
byte[] buff = new byte[16384];
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());
}
}
}
and the layout file as follows -
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:id="@+id/Logo"
android:orientation="vertical">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10px">
<TextView android:id="@+id/text_kb_streamed"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textStyle="bold"
android:text="...streaming audio..."/>
<Button android:id="@+id/button_stream"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10px"
style="?android:attr/buttonStyleSmall"
android:text="Start Streaming"/>
<ProgressBar android:id="@+id/progress_bar"
android:layout_width="200px"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
style="?android:attr/progressBarStyleHorizontal"/>
<ImageButton android:id="@+id/button_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5px"
style="?android:attr/buttonStyleSmall"
android:src="@drawable/button_pause"/>
</LinearLayout>
</LinearLayout>
<ViewFlipper
android:id="@+id/MainFlipper"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="@+id/Logo"
android:layout_marginBottom="37dip">
</ViewFlipper>
<FrameLayout
android:id="@+id/MediaPlayer"
android:layout_width="fill_parent"
android:layout_height="130dip"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true">
</FrameLayout>
</RelativeLayout>
But the same code does not work in Android 2.2. Can it be fixed up to run on 2.2 ?
Here's the error I am getting -
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): Error initializing the MediaPlayer.
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): java.io.IOException: setDataSourceFD failed.: status=0x80000000
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at android.media.MediaPlayer.setDataSource(Native Method)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at android.media.MediaPlayer.setDataSource(MediaPlayer.java:749)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at com.troubadorian.android.teststreaming.StreamingMediaPlayer.createMediaPlayer(StreamingMediaPlayer.java:272)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at com.troubadorian.android.teststreaming.StreamingMediaPlayer.startMediaPlayer(StreamingMediaPlayer.java:237)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at com.troubadorian.android.teststreaming.StreamingMediaPlayer.access$2(StreamingMediaPlayer.java:212)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at com.troubadorian.android.teststreaming.StreamingMediaPlayer$2.run(StreamingMediaPlayer.java:190)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at android.os.Handler.handleCallback(Handler.java:587)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at android.os.Handler.dispatchMessage(Handler.java:92)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at android.os.Looper.loop(Looper.java:123)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at android.app.ActivityThread.main(ActivityThread.java:4627)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at java.lang.reflect.Method.invokeNative(Native Method)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at java.lang.reflect.Method.invoke(Method.java:521)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at dalvik.system.NativeStart.main(Native Method)
I/global ( 1423): Default buffer size used in BufferedInputStream constructor. It would be better to be explicit if an 8k buffer is required.
I/global ( 1423): Default buffer size used in BufferedOutputStream constructor. It would be better to be explicit if an 8k buffer is required.
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): Buffered File path: /data/data/com.troubadorian.android.teststreaming/cache/playingMedia34.dat
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): Buffered File length: 193188
E/MediaPlayer( 1423): Unable to to create media player
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): Error initializing the MediaPlayer.
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): java.io.IOException: setDataSourceFD failed.: status=0x80000000
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at android.media.MediaPlayer.setDataSource(Native Method)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at android.media.MediaPlayer.setDataSource(MediaPlayer.java:749)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at com.troubadorian.android.teststreaming.StreamingMediaPlayer.createMediaPlayer(StreamingMediaPlayer.java:272)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at com.troubadorian.android.teststreaming.StreamingMediaPlayer.startMediaPlayer(StreamingMediaPlayer.java:237)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at com.troubadorian.android.teststreaming.StreamingMediaPlayer.access$2(StreamingMediaPlayer.java:212)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at com.troubadorian.android.teststreaming.StreamingMediaPlayer$2.run(StreamingMediaPlayer.java:190)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at android.os.Handler.handleCallback(Handler.java:587)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at android.os.Handler.dispatchMessage(Handler.java:92)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at android.os.Looper.loop(Looper.java:123)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at android.app.ActivityThread.main(ActivityThread.java:4627)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at java.lang.reflect.Method.invokeNative(Native Method)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at java.lang.reflect.Method.invoke(Method.java:521)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): at dalvik.system.NativeStart.main(Native Method)
I/global ( 1423): Default buffer size used in BufferedInputStream constructor. It would be better to be explicit if an 8k buffer is required.
I/global ( 1423): Default buffer size used in BufferedOutputStream constructor. It would be better to be explicit if an 8k buffer is required.
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): Buffered File path: /data/data/com.troubadorian.android.teststreaming/cache/playingMedia35.dat
E/com.troubadorian.android.teststreaming.StreamingMediaPlayer( 1423): Buffered File length: 194588
E/MediaPlayer( 1423): Unable to to create media player
Please help.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我稍微追踪了你的错误......
往下看Froyo的源代码,在frameworks/base/media/libmedia/mediaplayer.cpp中,归结为这个函数。
此时,显然
player
参数为 NULL,它来自函数
status_t MediaPlayer::setDataSource(const char *url, const KeyedVector*headers)
来自同一个文件,它能够实例化IMediaPlayerService
,但create
方法一定由于某种原因失败了......Android 文档:
使用 MediaPlayer 后,您应该调用 release() 来释放资源。如果不释放,过多的MediaPlayer实例可能会导致异常。
也许您应该检查
createMediaPlayer
函数仅被调用一次?我想 Eclair 也会失败,但可能值得研究一下...或者也许您正在查看的是所描述的内容 这里
希望有帮助
I traced your error a little bit...
Looking down in Froyo's source code, in frameworks/base/media/libmedia/mediaplayer.cpp, it comes down to this function.
At this point, apparently the
player
argument is NULLThat came from the function
status_t MediaPlayer::setDataSource(const char *url, const KeyedVector<String8, String8> *headers)
from the same file, where it was able to instantiate theIMediaPlayerService
, but thecreate
method must have failed for some reason...One thing from the Android doc :
When done with the MediaPlayer, you should call release(), to free the resources. If not released, too many MediaPlayer instances may result in an exception.
Maybe you should check the
createMediaPlayer
function is called only once ? I guess that would have failed also with Eclair, but may be worth looking into...Or maybe what you are looking at is what is described here
Hope that helps
我的 MediaPlayer 代码遇到了同样的错误。我在播放原始目录中的简单 test.ogg 文件时得到了各种结果。我的 AVD 能够播放该文件,SE Xperia Mini Pro 可以播放声音,但我的三星 SII 使用下面的简单代码崩溃了。
我收到了和您一样的错误,
我进一步测试了通过本地网络播放该文件,并获得了两部手机的预期结果(SE 播放了该文件,而三星无法播放该文件)。这几乎意味着不同的 Android 版本无法播放相同的声音,您需要使用不同的音频格式。
如果手机无法本地播放文件,我唯一的解决方法是重新采样音频。
I experienced the same error with my MediaPlayer code. I had various results playing simple test.ogg file located in the raw directory. My AVD's were able to play the file, SE Xperia Mini Pro played the sound, but my Samsung SII crashed using the simple code below.
I received the same error like you
I further tested playing the file over the local network with expected results for both phones (SE played the file and Samsung could not play it). This pretty much means that different Android versions are unable to play the same sound and you need to use difference audio format.
If the phone is unable to play the file natively, the only workaround for me is to resample the audio.