问题
i was trying to play AAC audio stream in android MediaPlayer, as mentioned here and also here author 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();
this implementation is seekAble and behave as it should according to position argument in MediaDataSource.readAt(int pos...)
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.
please clarify this behavior
回答1:
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
}
}
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 needs to much data before starting (30 packet as i tested) which correspond to 3 seconds (with my sample rate and bitrate).
来源:https://stackoverflow.com/questions/65528344/android-mediadatasource-unexpected-behavior