Android视频解码获取帧数据

一、获取视频轨道

设置视频资源MediaExtractor,获取轨道数量,然后遍历轨道数据,获取Video轨道MediaFormat。

mediaExtractor = new MediaExtractor();
        try {
            mediaExtractor.setDataSource(path);
        } catch (IOException e) {
            e.printStackTrace();
        }
        int count = mediaExtractor.getTrackCount();
        for (int i = 0; i < count; i++) {
            MediaFormat trackFormat = mediaExtractor.getTrackFormat(i);
            if (trackFormat.getString(MediaFormat.KEY_MIME).contains("video")) {
                videoFormat = trackFormat;
                mediaExtractor.selectTrack(i);
                break;
            }
        }

二、设置视频参数

视频解析中三个参数使必须设置的,不设置的话会导致:

catch exception:android.media.MediaCodec$CodecException: Error 0xfffffc0e

而且要设置正确,如果设备不支持宽高,也会抛出异常。

        //必须参数,否则会抛出catch exception:android.media.MediaCodec$CodecException: Error 0xfffffc0e
        int colorFormat = MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
        videoFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
        videoFormat.setInteger(MediaFormat.KEY_WIDTH, videoFormat.getInteger(MediaFormat.KEY_WIDTH) / 4);
        videoFormat.setInteger(MediaFormat.KEY_HEIGHT, videoFormat.getInteger(MediaFormat.KEY_HEIGHT) / 4);
        try {
            mediaCodec = MediaCodec.createDecoderByType(videoFormat.getString(MediaFormat.KEY_MIME));
        } catch (IOException e) {
            e.printStackTrace();
        }

三、获取MediaCodec进行编解码

        mediaCodec.setCallback(codecCallback);
        ImageReader imageReader = ImageReader.newInstance(
                videoFormat.getInteger(MediaFormat.KEY_WIDTH),
                videoFormat.getInteger(MediaFormat.KEY_HEIGHT),
                ImageFormat.YUV_420_888,
                3);
        //必须在setCallback之后
        mediaCodec.configure(videoFormat, imageReader.getSurface(), null, 0);
        mediaCodec.start();
      
    MediaCodec.Callback codecCallback = new MediaCodec.Callback() {
        @Override
        public void onInputBufferAvailable(@NonNull MediaCodec mediaCodec, int index) {
            ByteBuffer inputBuffer = mediaCodec.getInputBuffer(index);
            int size = mediaExtractor.readSampleData(inputBuffer, 0);
            LogUtils.d(size);
            if (size > 0) {
                long sampleTime = mediaExtractor.getSampleTime();
                mediaCodec.queueInputBuffer(index, 0, size, sampleTime, 0);
                mediaExtractor.advance();
            } else {
                mediaCodec.queueInputBuffer(index, 0, 0, 0L, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
            }
        }

        @Override
        public void onOutputBufferAvailable(@NonNull MediaCodec mediaCodec, int index, @NonNull MediaCodec.BufferInfo info) {
            // 获取输出缓冲(其中包含编解码后数据)
            ByteBuffer outputBuffer = mediaCodec.getOutputBuffer(index);
            MediaFormat bufferFormat = mediaCodec.getOutputFormat(index);
            //为false不会输出到setOnImageAvailableListener里面
            mediaCodec.releaseOutputBuffer(index, true);
        }

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

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

        }
    };

四、监听解码数据

        imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader reader) {
                Image image = reader.acquireLatestImage();
                if (image != null) {
                    int height = image.getHeight();
                    int width = image.getWidth();
                    Bitmap bitmap = imageToBitmap(image);
                    byte[] bytes = bitmapToByteArray(bitmap);
                    //一定要close否则buff溢出
                    image.close();
                }
            }
        }, null);

这块比较耗时,需要在子线程进行。

 

你可能感兴趣的:(媒体,视频,解码,音频编码解码)