问题描述
我试图在android MediaPlayer中播放AAC音频流,如此处以及作者声称问题在于忽略了位置参数,所以我做了一些设置来测试这一点,我将使用记录器进行记录并将其保存到缓冲区中,然后根据此
i was trying to play AAC audio stream in android MediaPlayer,as mentioned here and also hereauthor claimed the problem was ignoring position argument so i made a little setup to test this i will record using recorder and save it to a buffer and feed this buffer to MediaPlayer according to this
// parcel pipe: 1: write
// 0: read
ParcelFileDescriptor[] pfd = ParcelFileDescriptor.createPipe();
// a Good Recorder!
final MediaRecorder mMediaRecorder = new MediaRecorder();
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_WB); //for AMR Codec
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_WB);
//mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.AAC_ADTS); // for AAC codec
//mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mMediaRecorder.setAudioChannels(2);
mMediaRecorder.setAudioSamplingRate(44100);
mMediaRecorder.setAudioEncodingBitRate(96000);
mMediaRecorder.setOutputFile(pfd[1].getFileDescriptor());
mMediaRecorder.prepare();
mMediaRecorder.start();
// get the pipe output
InputStream inp = new ParcelFileDescriptor.AutoCloseInputStream(pfd[0]);
// populate buffer
byte[] buff = new byte[60*1024]; // 60 kb almost 5 second for AAC codec with above attributes
int i = 0;
while (i<buff.length){
i+= inp.read(buff,i,buff.length-i);
}
//write buffer to a file
FileOutputStream fos =new FileOutputStream(new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)+"/rec.ogg"));
fos.write(buff);
fos.close();
// play from buffer
MediaPlayer mp = new MediaPlayer();
mp.setLooping(true);
mp.setDataSource(new ByteBufferMediaSourceSimple(buff)); // explained in above article
mp.prepare();
mp.start();
此实现是seekAble,根据MediaDataSource.readAt(int pos ...)中的position参数,应按其行为运行
this implementation is seekAble and behave as it should according to position argument in MediaDataSource.readAt(int pos...)
如果我使用 AMR编解码器
,一切都会如期进行,但是当我尝试使用 AAC编解码器
MediaPlayer
给出 I/O错误{(1,-1004)}
但是我有足够的信心说我录制了一个可播放的缓冲区,因为保存的文件可以由MediaPlayer播放.
if i use AMR codec
everything goes as promised but when i try with AAC codec
MediaPlayer
gives I/O error {(1,-1004)}
but i'm confident enough to say i recorded a playable buffer because saved file is playable by MediaPlayer.
请澄清这种行为
推荐答案
根据 ADTS_Format缓冲区应包含整数个数据包,如果 MediaPlayer
到达任何不良的结构化数据包,则将引发错误,因此窍门是我们应该在缓冲之前将其打包,我可以确认我的解决方案对ADTS流有效:
according to ADTS_Format the buffer should contain an integer number of packets , MediaPlayer
will throw error if it reaches any bad structed packet so the trick is we should Packetize before buffering i can confirm my solution works on ADTS stream:
package CallModule.Media.Convertors;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import CallModule.Media.Objects.ADTSFrame;
import CallModule.Media.Objects.MediaObject;
public class InputStreamADTSFrameHarvest implements Packetize{
static private int readLen(byte[] first8bytes){
int four = first8bytes[3]&0xff; // 2 rightmost bits
int five = first8bytes[4]&0xff; // all of bits
int six = first8bytes[5]&0xff; // 3 leftmost bits
six >>=5; // easy! 3 left most bits : 0b1110 >> 1 : 0b111
four = ((four & 0b0011)<<11); // accept only 2 bits (rightmost)
five<<=3; // to get the value
return six+four+five;
}
static private byte FF = (byte) 0b11111111;
public static ADTSFrame harvest(InputStream inps) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] header = {FF,0,0,0,0,0,0,0};
int secondbyte;
int first;
while (true){
bos.reset();
first = inps.read();
if(FF == (byte) first){ // first byte is -1 (singed byte) second byte can be from F0 to FF (-16 to -1)
secondbyte = inps.read();
if(secondbyte>239 && secondbyte<256){ // we found the tail! now we need more 6 bytes to have total of 8 bytes;
header[1] =(byte)secondbyte;
for(int i=0;i<6;i++){
header[2+i] = (byte) inps.read();
}
bos.write(header);
int len = readLen(header);
byte[] body = new byte[len-8];
int res = 0;
while (res != len-8){
if(res !=0){
System.out.println("Delay");
}
res += inps.read(body,res,len-8-res);
}
bos.write(body);
break ;
}
}else{
System.out.println("Lost something");
}
}
ADTSFrame s = new ADTSFrame();
s.data= bos.toByteArray();
s.len = s.data.length;
return s;
}
@Override
public ADTSFrame packetize(InputStream inps) throws IOException {
return harvest(inps); // rapid call on this method and write to your buffer
}
}
因此,当我们将其保存到文件中(即使最后一个数据包格式错误)时,setDatasource方法(我想)也会在缓冲之前对其进行修剪
so when we save it to a file (even if last packet is malformed) the setDatasource method will (i guess) trim it before buffering
and MediaPlayer在开始之前需要大量数据(我测试了30个数据包)对应于3秒(包括我的采样率和比特率).
and MediaPlayer needs to much data before starting (30 packet as i tested)which correspond to 3 seconds (with my sample rate and bitrate).
这篇关于Android MediaDataSource意外行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!