Android在MediaPlayer中显示视频流并使用MediaCodec进行编码

kvg*_*vgr 6 video android android-mediaplayer mediacodec

问题:我有来自GoPro相机的视频流,格式为.m3u8.我需要在应用程序中显示流的内容,然后流式传输视频.对于流媒体,我有与MediaCodec和ffmpeg一起使用的库.我应该能够流式传输MediaCodec的outputBuffer.

我可以在SurfaceView上使用MediaPlayer流式传输视频,没问题.真正的困难在于使用MediaCodec.我可以初始化MediaCodec,获取表面并将其添加到MediaPlayer - 蒸汽播放 - 我可以听到声音.

现在的问题是:

  1. 在SurfaceView上显示流 - 或MP和MC可以使用的任何其他视图.从Surface到SurfaceView/TextureView显示数据的最佳方法是什么?

  2. 我无法从MediaCodec获取数据.我只需要支持5.0+设备,所以我尝试使用MediaCodec.Callback.但我每次都会得到错误.我还没有找到错误的含义.我可以做些什么来配合Callback工作?

  3. 有没有"简单"的方法,或者我必须深入到谷歌Grafika的缓冲区和表面?

以下是我得到的错误:

E/ACodec? [OMX.qcom.video.encoder.avc] storeMetaDataInBuffers (output) failed w/ err -2147483648

 1877-1904/com.example.marek.goprorecorder E/ACodec? [OMX.qcom.video.encoder.avc] ERROR(0x80001009)
07-15 15:54:47.411    1877-1904/com.example.marek.goprorecorder E/ACodec? signalError(omxError 0x80001009, internalError -2147483648)
07-15 15:54:47.411    1877-1903/com.example.marek.goprorecorder E/MediaCodec? Codec reported err 0x80001009, actionCode 0, while in state 6

Errror mess: android.media.MediaCodec$CodecException: Error 0x80001009
07-15 15:54:47.454    1877-1877/com.example.marek.goprorecorder D/MediaCodecCallback? Errror diag: android.media.MediaCodec.error_neg_2147479543
07-15 15:54:47.454    1877-1877/com.example.marek.goprorecorder D/MediaCodecCallback? Errror rec: false
07-15 15:54:47.454    1877-1877/com.example.marek.goprorecorder D/MediaCodecCallback? Errror tra: false
Run Code Online (Sandbox Code Playgroud)

这是我的活动:

public class MainActivity extends ActionBarActivity implements MediaPlayer.OnPreparedListener{
    private final MediaPlayer mp = new MediaPlayer();
    private Surface encoderSurface;
    private static final String MIME_TYPE = "video/avc";    // H.264 Advanced Video Coding
    private static final int FRAME_RATE = 30;               // 30fps
    private static final int IFRAME_INTERVAL = 0;           // 5 seconds between I-frames
    private static final File OUTPUT_DIR = Environment.getExternalStorageDirectory();
    MediaCodec encoder = null;
    MediaCodec.BufferInfo mBufferInfo;
    TextureView surfaceView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        int width = 432;
        int height = 240;
        prepareEncoder(width, height, 600000);
    }

    private void prepareEncoder(int width, int height, int bitRate) {
        mBufferInfo = new MediaCodec.BufferInfo();
        MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, width, height);
        format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
        format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
        format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);

        try {
            encoder = MediaCodec.createEncoderByType(MIME_TYPE);
        } catch (IOException e) {
            e.printStackTrace();
        }
        CodecCallback callback = new CodecCallback();
        encoder.setCallback(callback);
        encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
        encoderSurface = encoder.createInputSurface();
        preparePlayer(encoderSurface);
        encoder.start();
    }

    void preparePlayer(Surface s) {
        try {
            //mp.setDataSource("http://10.5.5.9:8080/live/amba.m3u8");
            mp.setDataSource("http://playertest.longtailvideo.com/adaptive/oceans_aes/oceans_aes.m3u8");
            mp.setSurface(s);
            mp.prepare();
            mp.setOnPreparedListener(this);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onPrepared(MediaPlayer mp) {
        mp.start();
    }

    private class CodecCallback extends Callback {

        @Override
        public void onInputBufferAvailable(MediaCodec codec, int index) {
            Log.d("MediaCodecCallback","InputBufferAvailable "+index);
        }

        @Override
        public void onOutputBufferAvailable(MediaCodec codec, int index, MediaCodec.BufferInfo info) {
            ByteBuffer outBuffer = codec.getOutputBuffer(index);
            Log.d("MediaCodecCallback", "OutputBuffer Position: " + outBuffer.position());
            Log.d("MediaCodecCallback", "OutputBuffer Limit: " + outBuffer.limit());
            Log.d("MediaCodecCallback", "OutputBuffer Capacity: " + outBuffer.capacity());
            Log.d("MediaCodecCallback","OutputBuffer: "+outBuffer.toString());
            encoder.releaseOutputBuffer(index, false);
        }

        @Override
        public void onError(MediaCodec codec, MediaCodec.CodecException e) {
            Log.d("MediaCodecCallback","Errror mess: " + e);
            Log.d("MediaCodecCallback","Errror diag: " + e.getDiagnosticInfo());
            Log.d("MediaCodecCallback","Errror rec: " + e.isRecoverable());
            Log.d("MediaCodecCallback","Errror tra: " + e.isTransient());
        }

        @Override
        public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
            Log.d("MediaCodecCallback","OutputFormatChanged: " + format.toString());
        }
    }


}
Run Code Online (Sandbox Code Playgroud)

编辑1:

这是将输出添加到SurfaceTexture的正确方法吗?

@Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        Surface s = new Surface(surface);
        preparePlayer(s); //ads Surface to MediaPlayer and starts to play the stream
    }
Run Code Online (Sandbox Code Playgroud)

我可以使用fromSurfaceTextureUpdate获得的surfaceTexture吗?

@Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {           

    }
Run Code Online (Sandbox Code Playgroud)
  • 另一个问题,你有例子如何将SurfaceTexture渲染到Surface吗?谢谢!