mp3再生の開始時のメディアプライヤースタッター
-
27-10-2019 - |
質問
生のリソースに保存されているMP3ファイルの再生に問題があります。ファイルが最初に再生を開始すると、おそらく4分の1秒のサウンドを生成してから再起動します。 (これは基本的に説明されている問題の複製であることを知っています ここ, 、しかし、そこに提供されたソリューションは私のために働いていません。)私はいくつかのことを試して、問題についていくらかの進歩を遂げましたが、それは完全に修正されていません。
これが私がファイルを再生するために設定する方法です:
mPlayer.reset();
try {
AssetFileDescriptor afd = getResources().openRawResourceFd(mAudioId);
if (afd == null) {
Toast.makeText(mOwner, "Could not load sound.",
Toast.LENGTH_LONG).show();
return;
}
mPlayer.setDataSource(afd.getFileDescriptor(),
afd.getStartOffset(), afd.getLength());
afd.close();
mPlayer.prepare();
} catch (Exception e) {
Log.d(LOG_TAG, "Could not load sound.", e);
Toast.makeText(mOwner, "Could not load sound.", Toast.LENGTH_LONG)
.show();
}
アクティビティを終了した場合(呼び出します mPlayer.release()
)そしてそれに戻って(新しい媒体を作成する)、st音は通常(常にではない)なくなっています -提供された 同じサウンドファイルをロードします。私は違いをもたらさないいくつかのことを試しました:
- サウンドファイルをリソースとしてではなく、資産としてロードします。
- 使用してMediaplayerを作成します
MediaPlayer.create(getContext(), mAudioId)
通話をスキップしますsetDataSource(...)
とprepare()
.
それから、LogCatが常にこの行が表示されていることに気付きました。
DEBUG/AudioSink(37): bufferCount (4) is too small and increased to 12
st音が明らかな拒絶によるものであるかどうか疑問に思いました。これにより、私は何か他のものを試してみました:
- 電話後
prepare()
, 、 電話mPlayer.start()
すぐに電話してくださいmPlayer.pause()
.
私の嬉しい驚きに、これは大きな効果がありました。大量のst音がなくなったことに加えて、その過程で実際に音が再生されることはありません。
しかし、私が電話するとき、それはまだ時々ぶつかります mPlayer.start()
まじ?実際に。さらに、これは巨大なクラッジのようです。この問題を完全にきれいに殺す方法はありますか?
編集 より詳しい情報;関連するかどうかはわかりません。私が電話した場合 pause()
再生中に、以前の位置を探して、電話してください start()
繰り返しますが、新しいポジションで演奏を開始する前に、それが一時停止された場所から少し(〜1/4秒)追加のサウンドが聞こえます。これは、より多くのバッファリングの問題を示しているようです。
また、1.6〜3.0のエミュレーターにst音(および一時停止したバッファー)の問題が現れます。
解決
Mediaplayerが内部で作成するバッファーは、プレッチされた圧縮データを保存するためではなく、減圧サンプルを保存するためのものです。あなたのst音は、減圧のためにより多くのmp3データをロードするため、I/Oの遅さから来ていると思います。
私は最近、ビデオ再生に関する同様の問題を解決しなければなりませんでした。ありがとう MediaPlayer
arbitrary意を演奏することができません InputStream
(APIは不思議なことに不自由です)私が思いついた解決策は、HTTPを介して(SDカード上)ローカルファイルを提供するための小さなインプロセスWebサーバーを書くことでした。 MediaPlayer
次に、フォームのURIを介してロードします http://127.0.0.1:8888/videofilename.
編集:
以下は、Mediaplayerインスタンスにコンテンツをフィードするために使用するStreamProxyクラスです。基本的な用途は、あなたがそれをインスタンス化し、それを開始()、そしてあなたのメディアプレーヤーを次のようなものと一緒に設定することです MediaPlayer.setDataSource("http://127.0.0.1:8888/localfilepath");
それはかなり実験的であり、おそらく完全にバグがないわけではないことに注意する必要があります。これは、あなたと同様の問題を解決するために書かれていました。つまり、Mediaplayerはダウンロードされているファイルを再生できないことです。この方法でファイルをローカルにストリーミングすると、その制限が回避されます(つまり、StreamProxyがMediaplayerにフィードしている間にファイルをダウンロードするスレッドがあります)。
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import android.os.AsyncTask;
import android.os.Looper;
import android.util.Log;
public class StreamProxy implements Runnable {
private static final int SERVER_PORT=8888;
private Thread thread;
private boolean isRunning;
private ServerSocket socket;
private int port;
public StreamProxy() {
// Create listening socket
try {
socket = new ServerSocket(SERVER_PORT, 0, InetAddress.getByAddress(new byte[] {127,0,0,1}));
socket.setSoTimeout(5000);
port = socket.getLocalPort();
} catch (UnknownHostException e) { // impossible
} catch (IOException e) {
Log.e(TAG, "IOException initializing server", e);
}
}
public void start() {
thread = new Thread(this);
thread.start();
}
public void stop() {
isRunning = false;
thread.interrupt();
try {
thread.join(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void run() {
Looper.prepare();
isRunning = true;
while (isRunning) {
try {
Socket client = socket.accept();
if (client == null) {
continue;
}
Log.d(TAG, "client connected");
StreamToMediaPlayerTask task = new StreamToMediaPlayerTask(client);
if (task.processRequest()) {
task.execute();
}
} catch (SocketTimeoutException e) {
// Do nothing
} catch (IOException e) {
Log.e(TAG, "Error connecting to client", e);
}
}
Log.d(TAG, "Proxy interrupted. Shutting down.");
}
private class StreamToMediaPlayerTask extends AsyncTask<String, Void, Integer> {
String localPath;
Socket client;
int cbSkip;
public StreamToMediaPlayerTask(Socket client) {
this.client = client;
}
public boolean processRequest() {
// Read HTTP headers
String headers = "";
try {
headers = Utils.readTextStreamAvailable(client.getInputStream());
} catch (IOException e) {
Log.e(TAG, "Error reading HTTP request header from stream:", e);
return false;
}
// Get the important bits from the headers
String[] headerLines = headers.split("\n");
String urlLine = headerLines[0];
if (!urlLine.startsWith("GET ")) {
Log.e(TAG, "Only GET is supported");
return false;
}
urlLine = urlLine.substring(4);
int charPos = urlLine.indexOf(' ');
if (charPos != -1) {
urlLine = urlLine.substring(1, charPos);
}
localPath = urlLine;
// See if there's a "Range:" header
for (int i=0 ; i<headerLines.length ; i++) {
String headerLine = headerLines[i];
if (headerLine.startsWith("Range: bytes=")) {
headerLine = headerLine.substring(13);
charPos = headerLine.indexOf('-');
if (charPos>0) {
headerLine = headerLine.substring(0,charPos);
}
cbSkip = Integer.parseInt(headerLine);
}
}
return true;
}
@Override
protected Integer doInBackground(String... params) {
long fileSize = GET CONTENT LENGTH HERE;
// Create HTTP header
String headers = "HTTP/1.0 200 OK\r\n";
headers += "Content-Type: " + MIME TYPE HERE + "\r\n";
headers += "Content-Length: " + fileSize + "\r\n";
headers += "Connection: close\r\n";
headers += "\r\n";
// Begin with HTTP header
int fc = 0;
long cbToSend = fileSize - cbSkip;
OutputStream output = null;
byte[] buff = new byte[64 * 1024];
try {
output = new BufferedOutputStream(client.getOutputStream(), 32*1024);
output.write(headers.getBytes());
// Loop as long as there's stuff to send
while (isRunning && cbToSend>0 && !client.isClosed()) {
// See if there's more to send
File file = new File(localPath);
fc++;
int cbSentThisBatch = 0;
if (file.exists()) {
FileInputStream input = new FileInputStream(file);
input.skip(cbSkip);
int cbToSendThisBatch = input.available();
while (cbToSendThisBatch > 0) {
int cbToRead = Math.min(cbToSendThisBatch, buff.length);
int cbRead = input.read(buff, 0, cbToRead);
if (cbRead == -1) {
break;
}
cbToSendThisBatch -= cbRead;
cbToSend -= cbRead;
output.write(buff, 0, cbRead);
output.flush();
cbSkip += cbRead;
cbSentThisBatch += cbRead;
}
input.close();
}
// If we did nothing this batch, block for a second
if (cbSentThisBatch == 0) {
Log.d(TAG, "Blocking until more data appears");
Thread.sleep(1000);
}
}
}
catch (SocketException socketException) {
Log.e(TAG, "SocketException() thrown, proxy client has probably closed. This can exit harmlessly");
}
catch (Exception e) {
Log.e(TAG, "Exception thrown from streaming task:");
Log.e(TAG, e.getClass().getName() + " : " + e.getLocalizedMessage());
e.printStackTrace();
}
// Cleanup
try {
if (output != null) {
output.close();
}
client.close();
}
catch (IOException e) {
Log.e(TAG, "IOException while cleaning up streaming task:");
Log.e(TAG, e.getClass().getName() + " : " + e.getLocalizedMessage());
e.printStackTrace();
}
return 1;
}
}
}
他のヒント
使用します prepareAsync
と応答します setOnPreparedListener
あなたの方が良いですか?アクティビティワークフローに応じて、 MediaPlayer
最初に初期化されます 準備リスナー そして、電話してください mPlayer.prepareAsync()
後でリソースを実際にロードしたら、そこで再生を開始します。ネットワークベースのストリーミングリソースには、同様のものを使用しています。
MediaPlayer m_player;
private ProgressDialog m_progressDialog = null;
...
try {
if (m_player != null) {
m_player.reset();
} else {
m_player = new MediaPlayer();
}
m_progressDialog = ProgressDialog
.show(this,
getString(R.string.progress_dialog_please_wait),
getString(R.string.progress_dialog_buffering),
true);
m_player.setOnPreparedListener(this);
m_player.setAudioStreamType(AudioManager.STREAM_MUSIC);
m_player.setDataSource(someSource);
m_player.prepareAsync();
} catch (Exception ex) {
}
...
public void onPrepared(MediaPlayer mp) {
if (m_progressDialog != null && m_progressDialog.isShowing()) {
m_progressDialog.dismiss();
}
m_player.start();
}
完全なソリューション(エラー処理など)には明らかにさらに多くのことがありますが、これはストリーミングを引き出すことができるから始める良い例として機能するはずだと思います。