android MediaCodec 的 MediaCodecBufferedOutput 索引为负

MediaCodecBufferedOutput index is negative for android MediaCodec

以下是一个代码片段,它应该从图像文件夹中读取所有图像并将它们编码为 h.262 视频并存储在 sdcard 中。我遵循了 android 文档(获取缓冲区、填充缓冲区、用于编码的队列缓冲区、出列输出缓冲区然后写入文件)。问题是当我将输出缓冲区出队时,我得到负索引,而它应该是 return 输出数据的索引。输出文件为 0 字节,未写入任何内容。 我对 mediaCodec 非常陌生。如有任何建议,我们将不胜感激。

MediaCodec mediaCodec=null;
    byte[] input = new byte[2000];
    BufferedOutputStream outputStream = null;

    try {
        //TODO
        //adjust parameters by consulting with hari sir

        mediaCodec = MediaCodec.createEncoderByType("video/avc");
        MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", 320, 240);
        mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 700000);
        mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 10);

        //not all phones support given color format, if color format is not supported app will crash with mediaCodec exception
        mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);
        mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);

        mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);


        mediaCodec.start();
        //after the mediaCodec is started we don't have ownership of input or output buffers

        Log.i("Codecinfo",""+mediaCodec.getCodecInfo());
        Log.i("Codecname",""+mediaCodec.getName());


    }
    catch (Exception e)
    {
        Log.e("ExceptionMediaCodec","Some exception in media codec");
    }

    //reached here
    System.out.println("mediacodec info="+mediaCodec.getCodecInfo());


    try {
        File ff = new File(Environment.getExternalStorageDirectory(), "Download/video_encoded.264");
        if (!ff.exists()) ff.createNewFile();
        System.out.println("H.264 output file initialized");

        outputStream = new BufferedOutputStream(new FileOutputStream(ff));
        Log.i("H264 avc Encoder", "outputStream initialized");
    } catch (Exception e){
        e.printStackTrace();
    }


    String path = Environment.getExternalStorageDirectory().toString()+"/images";
    File f = new File(path);
    Log.i("ExternalFileInfo",path.toString());
    //read image files onto an array
    File[] files = f.listFiles();
    System.out.println(files.getClass().getName());
    int NUM_IMAGES = files.length;
    String[] images = new String[NUM_IMAGES];
    for (int i=0;i<NUM_IMAGES;i++)
            images[i]=files[i].getName();



    for (String eachimage: images) {
        System.out.println(eachimage);
        byte[] eachByte = eachimage.getBytes();
        input = eachByte; //demo
        System.out.println("input byte initialized"+input.toString());
        try {
            System.out.println("Following is the content of byte array input");
            System.out.write(input);
        }catch (Exception e)
        {
            e.printStackTrace();
        }


    }

    //reached here
    System.out.println("image byte size="+input.length);
    //all images converted to bytearray


    ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();

    //System.out.println("inputBuffers="+(inputBuffers));
    //System.out.println("outputBuffers="+(outputBuffers));
    //reached here

    //returns the index of input buffer to be filled for encoding
    int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1); //-1 => wait indefinitely

    System.out.println("inputBufferedIndex="+inputBufferIndex); //0

    if (inputBufferIndex >= 0) {
        ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();

        ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];


        inputBuffer.clear();

        System.out.println("input byte placed in input buffer");
        inputBuffer.put(input);



        System.out.println("inputBuffer after filling up" + inputBuffer);


        mediaCodec.queueInputBuffer(inputBufferIndex, 0, input.length, System.nanoTime(), 0); //send each request with different timestamp


        System.out.println("mediacodec input  queued");

        }

    MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
    System.out.println("buffer info="+bufferInfo);

    int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, -1); //-ve value for indefinite waiting

    //reached here
    System.out.println("buffer info meta data=" + bufferInfo);


    System.out.println("outputBufferedIndex=" + outputBufferIndex);

        try {
            while (outputBufferIndex >= 0) {
                ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
                byte[] outData = new byte[bufferInfo.size];
                outputBuffer.get(outData);
                outputStream.write(outData, 0, outData.length);
                outputStream.flush();
                Log.i("AvcEncoder", outData.length + " bytes written");

                mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
                outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, -1);

            }
        } catch (Throwable t) {
            t.printStackTrace();
        }


    try {
        mediaCodec.stop();
        mediaCodec.release();

        outputStream.flush();
        outputStream.close();
    } catch (Exception e){
        e.printStackTrace();
    }

    System.out.println("Mediacodec="+mediaCodec);

请参阅 MediaCodec.dequeueOutputBuffer() 方法的文档,其中说:

Returns the index of an output buffer that has been successfully decoded or one of the INFO_* constants.

负值是 INFO_* 常量,可能是以下之一:

  • INFO_OUTPUT_BUFFERS_CHANGED
  • INFO_OUTPUT_FORMAT_CHANGED
  • INFO_TRY_AGAIN_LATER

最后一个不太可能,因为你要无限期地等待。

另外:在将一个缓冲区作为输入后,您不能总是依赖于等待一个输出缓冲区。只要编码器有可用的输入缓冲区,您就需要提供输入缓冲区,并消耗它提供给您的任何输出缓冲区。

最后几个输出缓冲区只有在您通过设置标志 BUFFER_FLAG_END_OF_STREAM.

表示您不会再提交任何输入缓冲区时才会输出