我正在制作一个非常简单的音乐应用程序,我试图找出如何做到这一点:
1)对于下载部分,我使用this example之后的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)
。原因如下:
有谁知道如何实现这一目标?在此先感谢您的帮助。