1227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks/*
2227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * Copyright (C) 2012 The Android Open Source Project
3227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks *
4227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * Licensed under the Apache License, Version 2.0 (the "License");
5227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * you may not use this file except in compliance with the License.
6227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * You may obtain a copy of the License at
7227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks *
8227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks *      http://www.apache.org/licenses/LICENSE-2.0
9227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks *
10227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * Unless required by applicable law or agreed to in writing, software
11227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * distributed under the License is distributed on an "AS IS" BASIS,
12227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * See the License for the specific language governing permissions and
14227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * limitations under the License.
15227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks */
16227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
17227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendrickspackage androidx.media.filterfw.decoder;
18227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
19227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport android.annotation.TargetApi;
20227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport android.graphics.SurfaceTexture;
21227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport android.graphics.SurfaceTexture.OnFrameAvailableListener;
22227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport android.media.MediaCodec;
23227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport android.media.MediaCodec.BufferInfo;
24227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport android.media.MediaFormat;
25227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport android.view.Surface;
26227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
27227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport androidx.media.filterfw.FrameImage2D;
28227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport androidx.media.filterfw.ImageShader;
29227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport androidx.media.filterfw.TextureSource;
30227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
31227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport java.nio.ByteBuffer;
32227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
33227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks/**
34227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * {@link TrackDecoder} that decodes a video track and renders the frames onto a
35227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * {@link SurfaceTexture}.
36227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks *
37227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * This implementation uses the GPU for image operations such as copying
38227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * and color-space conversion.
39227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks */
40227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks@TargetApi(16)
41227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendrickspublic class GpuVideoTrackDecoder extends VideoTrackDecoder {
42227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
43227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    /**
44227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks     * Identity fragment shader for external textures.
45227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks     */
46227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    private static final String COPY_FRAGMENT_SHADER =
47227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks            "#extension GL_OES_EGL_image_external : require\n" +
48227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks            "precision mediump float;\n" +
49227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks            "uniform samplerExternalOES tex_sampler_0;\n" +
50227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks            "varying vec2 v_texcoord;\n" +
51227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks            "void main() {\n" +
52227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks            "  gl_FragColor = texture2D(tex_sampler_0, v_texcoord);\n" +
53227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks            "}\n";
54227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
55227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    private final TextureSource mTextureSource;
56227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    private final SurfaceTexture mSurfaceTexture; // Access guarded by mFrameMonitor.
57227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    private final float[] mTransformMatrix;
58227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
59227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    private final int mOutputWidth;
60227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    private final int mOutputHeight;
61227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
62227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    private ImageShader mImageShader;
63227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
64227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    private long mCurrentPresentationTimeUs;
65227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
66227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    public GpuVideoTrackDecoder(
67227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks            int trackIndex, MediaFormat format, Listener listener) {
68227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        super(trackIndex, format, listener);
69227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
70227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        // Create a surface texture to be used by the video track decoder.
71227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        mTextureSource = TextureSource.newExternalTexture();
72227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        mSurfaceTexture = new SurfaceTexture(mTextureSource.getTextureId());
73227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        mSurfaceTexture.detachFromGLContext();
74227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        mSurfaceTexture.setOnFrameAvailableListener(new OnFrameAvailableListener() {
75227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks            @Override
76227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks            public void onFrameAvailable(SurfaceTexture surfaceTexture) {
77227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                markFrameAvailable();
78227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks            }
79227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        });
80227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
81227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        mOutputWidth = format.getInteger(MediaFormat.KEY_WIDTH);
82227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        mOutputHeight = format.getInteger(MediaFormat.KEY_HEIGHT);
83227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
84227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        mTransformMatrix = new float[16];
85227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    }
86227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
87227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    @Override
88227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    protected MediaCodec initMediaCodec(MediaFormat format) {
89227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        Surface surface = new Surface(mSurfaceTexture);
90227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        MediaCodec mediaCodec = MediaCodec.createDecoderByType(
91227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                format.getString(MediaFormat.KEY_MIME));
92227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        mediaCodec.configure(format, surface, null, 0);
93227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        surface.release();
94227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        return mediaCodec;
95227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    }
96227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
97227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    @Override
98227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    protected boolean onDataAvailable(
99227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks            MediaCodec codec, ByteBuffer[] buffers, int bufferIndex, BufferInfo info) {
100227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        boolean textureAvailable = waitForFrameGrab();
101227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
102227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        mCurrentPresentationTimeUs = info.presentationTimeUs;
103227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
104227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        // Only render the next frame if we weren't interrupted.
105227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        codec.releaseOutputBuffer(bufferIndex, textureAvailable);
106227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
107227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        if (textureAvailable) {
108227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks            if (updateTexture()) {
109227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                notifyListener();
110227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks            }
111227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        }
112227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
113227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        return false;
114227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    }
115227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
116227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    /**
117227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks     * Waits for the texture's {@link OnFrameAvailableListener} to be notified and then updates
118227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks     * the internal {@link SurfaceTexture}.
119227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks     */
120227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    private boolean updateTexture() {
121227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        // Wait for the frame we just released to appear in the texture.
122227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        synchronized (mFrameMonitor) {
123227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks            try {
124227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                while (!mFrameAvailable) {
125227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                    mFrameMonitor.wait();
126227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                }
127227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                mSurfaceTexture.attachToGLContext(mTextureSource.getTextureId());
128227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                mSurfaceTexture.updateTexImage();
129227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                mSurfaceTexture.detachFromGLContext();
130227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                return true;
131227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks            } catch (InterruptedException e) {
132227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                return false;
133227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks            }
134227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        }
135227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    }
136227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
137227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    @Override
138227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    protected void copyFrameDataTo(FrameImage2D outputVideoFrame, int rotation) {
139227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        TextureSource targetTexture = TextureSource.newExternalTexture();
140227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        mSurfaceTexture.attachToGLContext(targetTexture.getTextureId());
141227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        mSurfaceTexture.getTransformMatrix(mTransformMatrix);
142227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
143227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        ImageShader imageShader = getImageShader();
144227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        imageShader.setSourceTransform(mTransformMatrix);
145227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
146227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        int outputWidth = mOutputWidth;
147227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        int outputHeight = mOutputHeight;
148227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        if (rotation != 0) {
149227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks            float[] targetCoords = getRotationCoords(rotation);
150227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks            imageShader.setTargetCoords(targetCoords);
151227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks            if (needSwapDimension(rotation)) {
152227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                outputWidth = mOutputHeight;
153227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                outputHeight = mOutputWidth;
154227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks            }
155227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        }
156227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        outputVideoFrame.resize(new int[] { outputWidth, outputHeight });
157227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        imageShader.process(
158227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                targetTexture,
159227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                outputVideoFrame.lockRenderTarget(),
160227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                outputWidth,
161227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                outputHeight);
162227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        outputVideoFrame.setTimestamp(mCurrentPresentationTimeUs * 1000);
163227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        outputVideoFrame.unlock();
164227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        targetTexture.release();
165227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
166227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        mSurfaceTexture.detachFromGLContext();
167227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    }
168227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
169227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    @Override
170227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    public void release() {
171227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        super.release();
172227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        synchronized (mFrameMonitor) {
173227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks            mTextureSource.release();
174227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks            mSurfaceTexture.release();
175227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        }
176227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    }
177227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
178227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    /*
179227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks     * This method has to be called on the MFF processing thread.
180227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks     */
181227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    private ImageShader getImageShader() {
182227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        if (mImageShader == null) {
183227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks            mImageShader = new ImageShader(COPY_FRAGMENT_SHADER);
184227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks            mImageShader.setTargetRect(0f, 1f, 1f, -1f);
185227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        }
186227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        return mImageShader;
187227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    }
188227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
189227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    /**
190227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks     * Get the quad coords for rotation.
191227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks     * @param rotation applied to the frame, value is one of
192227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks     *   {ROTATE_NONE, ROTATE_90_RIGHT, ROTATE_180, ROTATE_90_LEFT}
193227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks     * @return coords the calculated quad coords for the given rotation
194227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks     */
195227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    private static float[] getRotationCoords(int rotation) {
196227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks         switch(rotation) {
197227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks             case MediaDecoder.ROTATE_90_RIGHT:
198227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                 return new float[] { 0f, 0f, 0f, 1f, 1f, 0f, 1f, 1f };
199227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks             case MediaDecoder.ROTATE_180:
200227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                 return new float[] { 1f, 0f, 0f, 0f, 1f, 1f, 0f, 1f };
201227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks             case MediaDecoder.ROTATE_90_LEFT:
202227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                 return new float[] { 1f, 1f, 1f, 0f, 0f, 1f, 0f, 0f };
203227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks             case MediaDecoder.ROTATE_NONE:
204227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                 return new float[] { 0f, 1f, 1f, 1f, 0f, 0f, 1f, 0f };
205227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks             default:
206227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                 throw new IllegalArgumentException("Unsupported rotation angle.");
207227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks         }
208227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks     }
209227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
210227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks}
211