Android MediaDataSource unexpected behavior

强颜欢笑 提交于 2021-01-07 02:39:18

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!