リモートサーバーからオーディオを再生するアプリケーションを作成しています。ストリーミング オーディオを実装する方法をいくつか試しましたが、どれも私には十分ではありません。それが私が試したことです:
MediaPlayer の単純な使用
何かのようなもの:
MediaPlayer player = new MediaPlayer();
player.setDataSource(context, Uri.parse("http://whatever.com/track.mp3"));
player.prepare();
player.start();
(またはprepareAsync
、関係ありません)
ただし、MediaPlayer
リモート コンテンツを再生する場合、標準は非常に不安定です。頻繁に再生が落ちたり停止したりしますが、これを処理することはできません。一方で、メディア キャッシングを実装したいと考えています。しかし、MediaPlayer からバッファリングされたコンテンツを取得してデバイスのどこかに保存する方法が見つかりませんでした。
カスタム バッファリングの実装
その後、メディア ファイルをチャンク単位でダウンロードし、それらを 1 つのローカル ファイルに結合して、このファイルを再生するというアイデアが生まれました。ファイル全体のダウンロードは、接続が悪いために遅くなる可能性があるため、最初に十分な数の断片をダウンロードしてから、再生を開始し、ローカル ファイルのダウンロードと追加を続行しても問題ありません。さらに、キャッシング機能も利用できます。
計画のように聞こえますが、常にうまくいくとは限りません。HTC Sensation XE では完全に動作しますが、4.1 タブレットでは、この最初の作品を終了した後に再生が停止することはありませんでした。わからない、なぜそうなのか。これについて質問しましたが、回答がありませんでした。
2 つの MediaPlayer を使用する
2 つの MediaPlayer インスタンスを作成し、それらを相互に変更しようとしました。ロジックは次のとおりです。
- 最初のメディアのダウンロードを開始する
- ダウンロードしたら、currentMediaPlayer 経由で再生を開始します。残りのメディアのダウンロードが続行されます
- ダウンロードした作品がほぼ再生されたら (終了の 1 秒前)、同じソース ファイルを使用して secondaryMediaPlayer を準備します (再生中に追加されたものと同じ)。
- currentMediaPlayer の終了の 261 ミリ秒前 – 一時停止し、セカンダリを開始し、セカンダリを現在として設定し、次のセカンダリ プレーヤーの準備をスケジュールします。
起源:
private static final String FILE_NAME="local.mp3";
private static final String URL = ...;
private static final long FILE_SIZE = 7084032;
private static final long PREPARE_NEXT_PLAYER_OFFSET = 1000;
private static final int START_NEXT_OFFSET = 261;
private static final int INIT_PERCENTAGE = 3;
private MediaPlayer mPlayer;
private MediaPlayer mSecondaryPlayer;
private Handler mHandler = new Handler();
public void startDownload() {
mDownloader = new Mp3Downloader(FILE_NAME, URL, getExternalCacheDir());
mDownloader.setDownloadListener(mInitDownloadListener);
mDownloader.startDownload();
}
private Mp3Downloader.DownloadListener mInitDownloadListener = new Mp3Downloader.DownloadListener() {
public void onDownloaded(long bytes) {
int percentage = Math.round(bytes * 100f / FILE_SIZE);
// Start playback when appropriate piece of media downloaded
if (percentage >= INIT_PERCENTAGE) {
mPlayer = new MediaPlayer();
try {
mPlayer.setDataSource(mDownloader.getDownloadingFile().getAbsolutePath());
mPlayer.prepare();
mPlayer.start();
mHandler.postDelayed(prepareSecondaryPlayerRunnable, mPlayer.getDuration() - PREPARE_NEXT_PLAYER_OFFSET);
mHandler.postDelayed(startNextPlayerRunnable, mPlayer.getDuration() - START_NEXT_OFFSET);
} catch (IOException e) {
Log.e(e);
}
mDownloader.setDownloadListener(null);
}
}
};
// Starting to prepare secondary MediaPlayer
private Runnable prepareSecondaryPlayerRunnable = new Runnable() {
public void run() {
mSecondaryPlayer = new MediaPlayer();
try {
mSecondaryPlayer.setDataSource(mDownloader.getDownloadingFile().getAbsolutePath());
mSecondaryPlayer.prepare();
mSecondaryPlayer.seekTo(mPlayer.getDuration() - START_NEXT_OFFSET);
} catch (IOException e) {
Log.e(e);
}
}
};
// Starting secondary MediaPlayer playback, scheduling creating next MediaPlayer
private Runnable startNextPlayerRunnable = new Runnable() {
public void run() {
mSecondaryPlayer.start();
mHandler.postDelayed(prepareSecondaryPlayerRunnable, mSecondaryPlayer.getDuration() - mPlayer.getCurrentPosition() - PREPARE_NEXT_PLAYER_OFFSET);
mHandler.postDelayed(startNextPlayerRunnable, mSecondaryPlayer.getDuration() - mPlayer.getCurrentPosition() - START_NEXT_OFFSET);
mPlayer.pause();
mPlayer.release();
mPlayer = mSecondaryPlayer;
}
};
繰り返しますが、計画のように聞こえますが、完全には機能しません。MediaPlayers を切り替える瞬間はかなり聞き取りやすいです。ここでは反対の状況があります。4.1 タブレットでは問題ありませんが、HTC Sensation では明らかな遅延があります。
また、さまざまなダウンロード手法を実装しようとしました。10Kb チャンクと MP3 フレームによるダウンロードを実装しました。正確にはわかりませんが、MP3 フレームの場合は seekTo と start の方がうまく機能するようです。でもそれはただの感覚で、説明がわかりません。
ストリーミングメディアプレーヤー
グーグルでこの言葉を何度か見て、この実装を見つけました: https://code.google.com/p/mynpr/source/browse/trunk/mynpr/src/com/webeclubbin/mynpr/StreamingMediaPlayer.java?r =18
それは誰もが使用するソリューションですか?
はいの場合、それは私にとってもうまく機能していないため、悲しいです. そして、実装に新鮮なアイデアは見当たりません。
だから、質問
アプリケーションにオーディオ ストリーミングをどのように実装していますか? このような問題に直面したのは私だけではないと思います。いくつかの良い習慣があるはずです。