MediaPlayer заикается на начало Mp3 Playmant
-
27-10-2019 - |
Вопрос
У меня была проблема, воспроизводившаяся файл MP3, хранящийся в необработанном ресурсе: когда файл впервые начинает играть, он генерирует, возможно, четверть секунды звука, а затем перезагружается. (Я знаю, что это в основном дубликат описанной проблемы здесь, но предложенное решение не сработало для меня.) Я попробовал несколько вещей и добился некоторого прогресса в проблеме, но оно не полностью исправлено.
Вот как я настраиваюсь, чтобы воспроизвести файл:
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()
) и вернуться к нему (создавая новый медиаплейер), заикание обычно (но не всегда) исчезает -при условии Я загружаю тот же звуковой файл. Я попробовал пару вещей, которые не имеют значения:
- Загрузите звуковой файл как актив, а не как ресурс.
- Создайте медиаплеера, используя
MediaPlayer.create(getContext(), mAudioId)
и пропустить звонкиsetDataSource(...)
а такжеprepare()
.
Затем я заметил, что LogCat всегда показывает эту строку примерно в то время, когда начинается воспроизведение:
DEBUG/AudioSink(37): bufferCount (4) is too small and increased to 12
Это заставило меня задуматься о том, связано ли заикание из -за очевидного отпор. Это привело меня к тому, чтобы попробовать что -то еще:
- После звонка
prepare()
, вызовmPlayer.start()
и сразу же позвонитеmPlayer.pause()
.
К моему приятному сюрпризу, это имело большой эффект. Большая часть заикания исчезла, плюс нет звука (который я слышу) на самом деле воспроизводится в этот момент процесса.
Тем не менее, это все еще заикается время от времени, когда я звоню mPlayer.start()
серьезно. Плюс, это кажется огромным Kludge. Есть ли способ убить эту проблему полностью и чисто?
РЕДАКТИРОВАТЬ Больше информации; не уверен, если связано. Если я позвоню pause()
Во время воспроизведения, стремитесь к более ранней должности и позвоните start()
Опять же, я слышу короткий бит (~ 1/4 секунды) дополнительного звука, откуда он был приостановлен, прежде чем он начал играть на новой позиции. Это, кажется, указывает на большее количество проблем с буферизацией.
Кроме того, проблемы заикания (и приостановки буфера) появляются на эмуляторах с 1,6 до 3.0.
Решение
Afaik The Buffers, которые MediaPlayer создает внутри страны, предназначены для хранения декомпрессированных образцов, а не для хранения предварительных сжатых данных. Я подозреваю, что ваше заикание происходит от медлительности ввода/вывода, поскольку он загружает больше данных MP3 для декомпрессии.
Недавно мне пришлось решить аналогичную проблему с воспроизведением видео. Благодаря MediaPlayer
неспособность играть в произвольную InputStream
(API странно хромой) Решение, которое я придумал, заключалось в том, чтобы написать небольшой веб-сервер в процессе для обслуживания локальных файлов (на SD-карте) над HTTP. MediaPlayer
затем загружает его через URI формы http://127.0.0.1:8888/videofileename.
РЕДАКТИРОВАТЬ:
Ниже приведен класс StreamProxy, который я использую для подачи контента в экземпляр MediaPlayer. Основное использование заключается в том, что вы создаете его создание, запустите его и устанавливаете свой медиаплеер с чем -то вроде 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();
}
Очевидно, что в полном решении есть больше (обработка ошибок и т. Д.), Но я думаю, что это должно работать как хороший пример, который вы можете извлечь из этого.