Android 音视频知识点(六)

70 阅读1分钟

本文着重介绍 MediaCodec 解码视频的异步方式。异步方式在 Android 5.0 后提供的解码能力。相较同步方式,异步方式更加灵活,无需维护独立线程,无需人为控制入队出队逻辑,使用更加方便等优势。

代码实现

private void codecAsync() {
        String fileName = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath() +
                "/VideoDemo/80s_TestVideo.mp4";
        Log.e(TAG, "fileName: " + fileName);

        try {
            mediaExtractor = new MediaExtractor();
            mediaExtractor.setDataSource(fileName);

            int selectTrackIndex = 0;
            String mime = "";
            MediaFormat format = null;
            int videoWidth = 0;
            int videoHeight = 0;
            for (int i = 0; i < mediaExtractor.getTrackCount(); i++) {
                selectTrackIndex = i;
                format = mediaExtractor.getTrackFormat(i);
                mime = format.getString(MediaFormat.KEY_MIME);
                Log.i(TAG, "mime: " + mime);
                if (mime.startsWith("video/")) {
                    videoWidth = format.getInteger(MediaFormat.KEY_WIDTH);
                    videoHeight = format.getInteger(MediaFormat.KEY_HEIGHT);
                    break;
                }
            }

            mediaExtractor.selectTrack(selectTrackIndex);
            int frameRate = format.getInteger(MediaFormat.KEY_FRAME_RATE);
            long frameIntervalMs = (long)(1000.0/frameRate);

            Log.i(TAG, "videoWidth: " + videoWidth + "  videoHeight: " + videoHeight);
            float sizeScale = videoWidth * 1f / videoHeight;
            Log.d(TAG, "sizeScale: " + sizeScale);
            LinearLayout.LayoutParams layoutParams =
                    (LinearLayout.LayoutParams)surface_media_codec.getLayoutParams();
            int viewWidth = surface_media_codec.getMeasuredWidth();
            int viewHeight = (int)(viewWidth / sizeScale);
            Log.i(TAG, "viewWidth: " + viewWidth + "  viewHeight: " + viewHeight);
            layoutParams.height = viewHeight;
            surface_media_codec.setLayoutParams(layoutParams);
            surface_media_codec.requestLayout();

            Log.i(TAG, "final mime: " + mime);
            mediaCodec = MediaCodec.createDecoderByType(mime);
            mediaCodec.configure(format, surfaceHolder.getSurface(), null, 0);
            mediaCodec.setCallback(new MediaCodec.Callback() {
                @Override
                public void onInputBufferAvailable(@NonNull MediaCodec mediaCodec, int i) {
                    ByteBuffer buffer = mediaCodec.getInputBuffer(i);
                    int bufferSize = mediaExtractor.readSampleData(buffer, 0);
                    if (frameIdx == -1 || bufferSize <= 0) {
                        mediaCodec.queueInputBuffer(i, 0, 0,0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                        return;
                    }
                    mediaCodec.queueInputBuffer(i, 0, bufferSize,
                            mediaExtractor.getSampleTime() * 1000, 0);
                    mediaExtractor.advance();
                    ++frameIdx;
                }

                @Override
                public void onOutputBufferAvailable(@NonNull MediaCodec mediaCodec, int i,
                                                    @NonNull MediaCodec.BufferInfo bufferInfo) {
                    if (frameIdx == -1 && (bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                        return;
                    }

                    long currentTimeMs = System.currentTimeMillis();
                    long delay = frameIntervalMs - (currentTimeMs = lastFrameTimeMs);
                    if (delay > 0) {
                        try {
                            Thread.sleep(delay);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    lastFrameTimeMs = currentTimeMs;
                    mediaCodec.releaseOutputBuffer(i, true);
                }

                @Override
                public void onError(@NonNull MediaCodec mediaCodec,
                                    @NonNull MediaCodec.CodecException e) {

                }

                @Override
                public void onOutputFormatChanged(@NonNull MediaCodec mediaCodec,
                                                  @NonNull MediaFormat mediaFormat) {

                }
            });
            mediaCodec.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

详细实现参考代码库:e.coding.net/g-ithd5590/…