私は非常に単純な音楽アプリを作成しています。これを行う方法を見つけようとしています:
- オーディオ ファイルをダウンロードし、ファイルをローカルに録音する
- いつでも、ファイルからオーディオ フレームを抽出します (ダウンロード中またはダウンロード後)。
1)ダウンロード部分については、この例に従って Retrofit を使用します。簡単に言うと、ファイルをダウンロードし、ダウンロード中にローカルに記録することができます (そのため、ファイルのデータにアクセスするためにダウンロードの終了を待つ必要はありません)。
2)フレーム抽出部分については、次のように使用MediaExtractor
します。MediaCodec
MediaCodec codec;
MediaExtractor extractor;
MediaFormat format;
ByteBuffer[] codecInputBuffers;
ByteBuffer[] codecOutputBuffers;
Boolean sawInputEOS = false;
Boolean sawOutputEOS = false;
AudioTrack mAudioTrack;
MediaCodec.BufferInfo info;
File outputFile = null;
FileDescriptor fileDescriptor = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// the file being downloaded:
outputFile = new File(directory, "test.mp3");
try {
FileInputStream fileInputStream = new FileInputStream(outputFile);
fileDescriptor = fileInputStream.getFD();
}
catch (Exception e) {}
}
// Called once when enough data to extract.
private void onAudioFileReady() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// thread :
Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO);
// audio :
extractor = new MediaExtractor();
// the extractor only extracts the already downloaded part of the file:
try {
// extractor.setDataSource(url);
// extractor.setDataSource(outputFile.getAbsolutePath());
// extractor.setDataSource(MainActivity.this, Uri.parse(outputFile.getAbsolutePath()), null);
extractor.setDataSource(fileDescriptor);
}
catch (IOException e) {}
format = extractor.getTrackFormat(0);
String mime = format.getString(MediaFormat.KEY_MIME);
int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
try {
codec = MediaCodec.createDecoderByType(mime);
}
catch (IOException e) {}
codec.configure(format, null, null, 0);
codec.start();
codecInputBuffers = codec.getInputBuffers();
codecOutputBuffers = codec.getOutputBuffers();
extractor.selectTrack(0);
int minBufferSize = AudioTrack.getMinBufferSize(
sampleRate,
AudioFormat.CHANNEL_OUT_STEREO,
AudioFormat.ENCODING_PCM_16BIT);
mAudioTrack = new AudioTrack(
AudioManager.STREAM_MUSIC,
sampleRate,
AudioFormat.CHANNEL_OUT_STEREO,
AudioFormat.ENCODING_PCM_16BIT,
minBufferSize,
AudioTrack.MODE_STREAM
);
info = new MediaCodec.BufferInfo();
mAudioTrack.play();
do {
input();
output();
}
while (!sawInputEOS);
}
});
thread.start();
}
private void input() {
int inputBufferIndex = codec.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0) {
ByteBuffer byteBuffer = codecInputBuffers[inputBufferIndex];
int sampleSize = extractor.readSampleData(byteBuffer, 0);
long presentationTimeUs = 0;
if (sampleSize < 0) {
Log.w(LOG_TAG, "Saw input end of stream!");
sampleSize = 0;
}
else {
presentationTimeUs = extractor.getSampleTime();
}
codec.queueInputBuffer(inputBufferIndex,
0,
sampleSize,
presentationTimeUs,
sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
// doesn't seem to work:
extractor.advance();
}
}
private void output() {
final int res = codec.dequeueOutputBuffer(info, -1);
if (res >= 0) {
ByteBuffer buf = codecOutputBuffers[res];
final byte[] chunk = new byte[info.size];
buf.get(chunk);
buf.clear();
if (chunk.length > 0) {
mAudioTrack.write(chunk, 0, chunk.length);
}
codec.releaseOutputBuffer(res, false);
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
sawOutputEOS = true;
}
}
else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
codecOutputBuffers = codec.getOutputBuffers();
}
else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
final MediaFormat oformat = codec.getOutputFormat();
mAudioTrack.setPlaybackRate(oformat.getInteger(MediaFormat.KEY_SAMPLE_RATE));
}
}
機能:
がonAudioFileReady()
呼び出されると、このコードはファイルのオーディオ サンプルを抽出して再生しますが、既にダウンロードされたもののみを再生します。すでにダウンロードされた部分の最後に到達すると、利用可能なデータがさらにある場合でもMediaExtractor
停止します(extractor.advance()
抽出を続行したくないようです...)。
私が達成したいこと:
もちろん、十分なデータがある限り、ファイルのオーディオ サンプルの抽出を続行できるようにしたいと考えています。
重要:
その時点で、なぜ私が単に を使用しないのかと尋ねるかもしれませんextractor.setDataSource(url)
。理由は次のとおりです。
- 後で再生できるように、オーディオ ファイルをローカルに保存したい
- ダウンロード開始後もずっと再生できるようにしたい
誰もそれを達成する方法を知っていますか? よろしくお願いします。