Skip to content Skip to sidebar Skip to footer

Get Video Pixel Data From Exoplayer In Android

Is it possible to grab the pixel data (e.g. as RGB byte array) from a running video within the ExoPlayer? Ideally as the real video resolution and not the size as the shown View. I

Solution 1:

WARNING this solution currently only works on my Android 8 emulator because of this.

Ok, here is my solution. I'm using the SimpleExoPlayer with a custom MediaCodecVideoRenderer. I'm not binding the player to a SimpleExoPlayerView because I'm not allowed to if I want to manually grab the image data. Within my custom MediaCodecVideoRenderer I override the processOutputBuffer and use getOutputImage to get a nice standard Android Image. I then send it through my OpenCV code and then transform it back into an Android Bitmap using the OpenCV Utils whenever I need. Careful this code requires API >= 21. Also this codes does not output any audio.

privatevoidstartPlayer(Context context, Uri uri) {

    BandwidthMeterbandwidthMeter=newDefaultBandwidthMeter();
    TrackSelection.FactoryvideoTrackSelectionFactory=newAdaptiveTrackSelection.Factory(bandwidthMeter);
    TrackSelectortrackSelector=newDefaultTrackSelector(videoTrackSelectionFactory);

    SimpleExoPlayerplayer= ExoPlayerFactory.newSimpleInstance((eventHandler, videoRendererEventListener, audioRendererEventListener, textRendererOutput, metadataRendererOutput) -> {
        returnnewRenderer[]{newCustomMediaCodecVideoRenderer(context, MediaCodecSelector.DEFAULT, 1000, eventHandler, videoRendererEventListener, 100)};
    }, trackSelector);

    DataSource.FactorydataSourceFactory=newDefaultDataSourceFactory(context, Util.getUserAgent(context, context.getPackageName()));
    ExtractorMediaSourcevideoSource=newExtractorMediaSource.Factory(dataSourceFactory).createMediaSource(uri);

    LoopingMediaSourceloopingMediaSource=newLoopingMediaSource(videoSource);

    player.setPlayWhenReady(true);
    player.prepare(loopingMediaSource);

}

classCustomMediaCodecVideoRendererextendsMediaCodecVideoRenderer {
    CustomMediaCodecVideoRenderer(Context context, MediaCodecSelector mediaCodecSelector, long allowedJoiningTimeMs, @Nullable Handler eventHandler, @Nullable VideoRendererEventListener eventListener, int maxDroppedFrameCountToNotify) {
        super(context, mediaCodecSelector, allowedJoiningTimeMs, eventHandler, eventListener, maxDroppedFrameCountToNotify);
    }

    @OverrideprotectedbooleanprocessOutputBuffer(long positionUs, long elapsedRealtimeUs, MediaCodec codec, ByteBuffer buffer, int bufferIndex, int bufferFlags, long bufferPresentationTimeUs, boolean shouldSkip)throws ExoPlaybackException {


        Imageimage= codec.getOutputImage(bufferIndex);

        if (image != null) {
            Log.d(MainActivity.class.getName(), "test");

            Matmat= convertYuv420888ToMat(image, false);
            // TODO do your thing with the OpenCV Mat
        }

        returnsuper.processOutputBuffer(positionUs, elapsedRealtimeUs, codec, buffer, bufferIndex, bufferFlags, bufferPresentationTimeUs, shouldSkip);
    }
}

I found the Image to Mat conversion here.

private Mat convertYuv420888ToMat(Image image, boolean isGreyOnly) {
    intwidth= image.getWidth();
    intheight= image.getHeight();

    Image.PlaneyPlane= image.getPlanes()[0];
    intySize= yPlane.getBuffer().remaining();

    if (isGreyOnly) {
        byte[] data = newbyte[ySize];
        yPlane.getBuffer().get(data, 0, ySize);

        MatgreyMat=newMat(height, width, CvType.CV_8UC1);
        greyMat.put(0, 0, data);

        return greyMat;
    }

    Image.PlaneuPlane= image.getPlanes()[1];
    Image.PlanevPlane= image.getPlanes()[2];

    // be aware that this size does not include the padding at the end, if there is any// (e.g. if pixel stride is 2 the size is ySize / 2 - 1)intuSize= uPlane.getBuffer().remaining();
    intvSize= vPlane.getBuffer().remaining();

    byte[] data = newbyte[ySize + (ySize/2)];

    yPlane.getBuffer().get(data, 0, ySize);

    ByteBufferub= uPlane.getBuffer();
    ByteBuffervb= vPlane.getBuffer();

    intuvPixelStride= uPlane.getPixelStride(); //stride guaranteed to be the same for u and v planesif (uvPixelStride == 1) {
        uPlane.getBuffer().get(data, ySize, uSize);
        vPlane.getBuffer().get(data, ySize + uSize, vSize);

        MatyuvMat=newMat(height + (height / 2), width, CvType.CV_8UC1);
        yuvMat.put(0, 0, data);
        MatrgbMat=newMat(height, width, CvType.CV_8UC3);
        Imgproc.cvtColor(yuvMat, rgbMat, Imgproc.COLOR_YUV2RGB_I420, 3);
        yuvMat.release();
        return rgbMat;
    }

    // if pixel stride is 2 there is padding between each pixel// converting it to NV21 by filling the gaps of the v plane with the u values
    vb.get(data, ySize, vSize);
    for (inti=0; i < uSize; i += 2) {
        data[ySize + i + 1] = ub.get(i);
    }

    MatyuvMat=newMat(height + (height / 2), width, CvType.CV_8UC1);
    yuvMat.put(0, 0, data);
    MatrgbMat=newMat(height, width, CvType.CV_8UC3);
    Imgproc.cvtColor(yuvMat, rgbMat, Imgproc.COLOR_YUV2RGB_NV21, 3);
    yuvMat.release();
    return rgbMat;
}

Post a Comment for "Get Video Pixel Data From Exoplayer In Android"