Android MediaDataSource意外行为 [英] Android MediaDataSource unexpected behavior

查看:272
本文介绍了Android MediaDataSource意外行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在android MediaPlayer中播放AAC音频流,如此处以及

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();

此实现是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意外行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆