Java 在Android上解码/播放视频流并录制文件的最佳实践

Java 在Android上解码/播放视频流并录制文件的最佳实践,java,android,h.264,Java,Android,H.264,我正在编写一个应用程序,通过TCP连接收集h264流,并尽可能快地将其显示在TextureView上(可能越快越好)。 它工作得很好,但我打赌它可以做得更好。现在,我需要添加一个录音功能,我正在猜测如何在不损失表演的情况下做到这一点。 这是我的代码: public void run() { byte[] nal = new byte[NAL_SIZE_INC]; int nalLen = 0; int numZeroes = 0;

我正在编写一个应用程序,通过TCP连接收集h264流,并尽可能快地将其显示在
TextureView上(可能越快越好)。
它工作得很好,但我打赌它可以做得更好。现在,我需要添加一个录音功能,我正在猜测如何在不损失表演的情况下做到这一点。
这是我的代码:

    public void run() {
        byte[] nal = new byte[NAL_SIZE_INC];
        int nalLen = 0;
        int numZeroes = 0;
        int numReadErrors = 0;
        int connection_retries = 0;

        try {
            decoder = MediaCodec.createDecoderByType("video/avc");

            byte[] buffer = new byte[BUFFER_SIZE];

            do {
                if(camera.getStreamingProtocol() == Protocol.UDP)
                    reader = new UdpListner(camera.getPort());
                else //if(camera.getStreamingProtocol() == Protocol.TCP)
                    reader = new TcpIpReader(camera.getAddress(), camera.getPort());

                connection_retries++;
                Thread.sleep(VIDEO_STREAM_RESPAWN);
            } while (!reader.isConnected() && (connection_retries < MAX_CONN_RETRIES));

            if (!reader.isConnected()) {
                //throw new Exception();
                Log.d(getClass().getName(), "Link respawn: " + connection_retries);
                return;
            }

            while (keep_running) {
                int len = reader.read(buffer);
                if (!keep_running) break;

                if (len > 0) {
                    numReadErrors = 0;
                    for (int i = 0; i < len && keep_running; i++) {
                        if (nalLen == nal.length) {
                            nal = Arrays.copyOf(nal, nal.length + NAL_SIZE_INC);
                        }
                        nal[nalLen++] = buffer[i];

                        if (buffer[i] == 0) {
                            numZeroes++;
                        } else {
                            if (buffer[i] == 1 && numZeroes == 3) {
                                if (nalLen > 4) {
                                    int nalType = processNal(nal, nalLen - 4);
                                    if (!keep_running) break;
                                    if (nalType == -1) {
                                        nal[0] = nal[1] = nal[2] = 0;
                                        nal[3] = 1;
                                    }
                                }
                                nalLen = 4;
                            }
                            numZeroes = 0;
                        }
                    }
                } else {
                    numReadErrors++;
                    if (numReadErrors >= MAX_READ_ERRORS) {
                        setVideoMessage(R.string.error_lost_connection);
                        break;
                    }
                }

                if (format != null && decoding) {
                    if (!keep_running) break;
                    MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
                    int index;
                    do {
                        index = decoder.dequeueOutputBuffer(info, 0);
                        if (!keep_running) break;
                        if (index >= 0) {
                            decoder.releaseOutputBuffer(index, true);
                        }
                    } while (index >= 0);
                }
            }
        } catch (Exception ex) {
            Log.e(getClass().getName(), "Exception: " + ex.toString());
            if (reader == null || !reader.isConnected()) {
                setVideoMessage(R.string.error_couldnt_connect);
                finishHandler.postDelayed(finishRunner, FINISH_TIMEOUT);
            } else {
                setVideoMessage(R.string.error_lost_connection);
            }
            ex.printStackTrace();
        }

        if (reader != null) {
            try {
                reader.close();
            } catch (Exception ex) {
                Log.i(getClass().getName(), "Exception: " + ex.getMessage());
            }
            reader = null;
        }

        if (decoder != null) {
            try {
                setDecodingState(false);
                decoder.release();
            } catch (Exception ex) {
                Log.e(getClass().getName(), "Exception: " + ex.getMessage());
            }
            decoder = null;
        }
    }
public void run(){
byte[]nal=新字节[nal_SIZE_INC];
int-nalLen=0;
int numZeroes=0;
int numReadErrors=0;
int连接重试次数=0;
试一试{
解码器=MediaCodec.createDecoderByType(“视频/avc”);
字节[]缓冲区=新字节[缓冲区大小];
做{
if(camera.getStreamingProtocol()==Protocol.UDP)
reader=newudplistner(camera.getPort());
else//if(camera.getStreamingProtocol()==Protocol.TCP)
reader=新的tcpipreder(camera.getAddress(),camera.getPort());
连接重试++;
线程。睡眠(视频、流、重生);
}而(!reader.isConnected()&&(连接重试次数<最大连接重试次数));
如果(!reader.isConnected()){
//抛出新异常();
Log.d(getClass().getName(),“链接重新启动:”+连接重试);
返回;
}
while(保持运行){
int len=reader.read(缓冲区);
如果(!保持运行)中断;
如果(len>0){
numReadErrors=0;
对于(int i=0;i4){
int-nalType=processNal(nal,nalLen-4);
如果(!保持运行)中断;
如果(nalType==-1){
nal[0]=nal[1]=nal[2]=0;
nal[3]=1;
}
}
纳伦=4;
}
numZeroes=0;
}
}
}否则{
numReadErrors++;
如果(numReadErrors>=最大读取错误){
setVideoMessage(R.string.error\u失去连接);
打破
}
}
if(格式!=null&解码){
如果(!保持运行)中断;
MediaCodec.BufferInfo=新的MediaCodec.BufferInfo();
整数指数;
做{
index=decoder.dequeueOutputBuffer(info,0);
如果(!保持运行)中断;
如果(索引>=0){
解码器.releaseOutputBuffer(索引,true);
}
}而(指数>=0);
}
}
}捕获(例外情况除外){
Log.e(getClass().getName(),“异常:+ex.toString());
if(reader==null | |!reader.isConnected()){
setVideoMessage(R.string.error\u couldn\u connect);
postDelayed(finishRunner,完成超时);
}否则{
setVideoMessage(R.string.error\u失去连接);
}
例如printStackTrace();
}
if(读卡器!=null){
试一试{
reader.close();
}捕获(例外情况除外){
Log.i(getClass().getName(),“异常:+ex.getMessage());
}
reader=null;
}
if(解码器!=null){
试一试{
setDecodingState(假);
decoder.release();
}捕获(例外情况除外){
Log.e(getClass().getName(),“异常:+ex.getMessage());
}
解码器=空;
}
}
当然,这是
run()
方法的
线程
派生对象,它管理显示视频的
片段上的作业

创建一个简单的缓冲输出流并在其上写入从传入流收集的每个
缓冲区
,应该可以正常工作,否则可能会延迟视频渲染

我必须将头数据添加到h264输出文件以使其可读

有没有办法优化接收/显示部分?我愿意接受任何建议。。。我想用UDP来代替TCP,只是想提一下

致以最良好的祝愿, 迈克