1ea0bad0574451212591841ba84f477ecc216003aHuahui Wu/*
2ea0bad0574451212591841ba84f477ecc216003aHuahui Wu * Copyright (C) 2011 The Android Open Source Project
3ea0bad0574451212591841ba84f477ecc216003aHuahui Wu *
4ea0bad0574451212591841ba84f477ecc216003aHuahui Wu * Licensed under the Apache License, Version 2.0 (the "License");
5ea0bad0574451212591841ba84f477ecc216003aHuahui Wu * you may not use this file except in compliance with the License.
6ea0bad0574451212591841ba84f477ecc216003aHuahui Wu * You may obtain a copy of the License at
7ea0bad0574451212591841ba84f477ecc216003aHuahui Wu *
8ea0bad0574451212591841ba84f477ecc216003aHuahui Wu *      http://www.apache.org/licenses/LICENSE-2.0
9ea0bad0574451212591841ba84f477ecc216003aHuahui Wu *
10ea0bad0574451212591841ba84f477ecc216003aHuahui Wu * Unless required by applicable law or agreed to in writing, software
11ea0bad0574451212591841ba84f477ecc216003aHuahui Wu * distributed under the License is distributed on an "AS IS" BASIS,
12ea0bad0574451212591841ba84f477ecc216003aHuahui Wu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ea0bad0574451212591841ba84f477ecc216003aHuahui Wu * See the License for the specific language governing permissions and
14ea0bad0574451212591841ba84f477ecc216003aHuahui Wu * limitations under the License.
15ea0bad0574451212591841ba84f477ecc216003aHuahui Wu */
16ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
17ea0bad0574451212591841ba84f477ecc216003aHuahui Wupackage com.android.mediadump;
18ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
19ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport java.io.IOException;
20ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport java.io.BufferedOutputStream;
21ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport java.io.BufferedWriter;
22ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport java.io.File;
23ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport java.io.FileWriter;
24ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport java.io.FilenameFilter;
25ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport java.io.FileOutputStream;
26ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport java.io.File;
27ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
28ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport java.lang.Integer;
29ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport java.lang.Math;
30ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport java.nio.ByteBuffer;
31ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport java.nio.ByteOrder;
32ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport java.nio.FloatBuffer;
33ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport java.nio.channels.FileChannel;
34ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport java.nio.IntBuffer;
35ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport java.util.Properties;
36ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
37ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport javax.microedition.khronos.egl.EGLConfig;
38ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport javax.microedition.khronos.opengles.GL10;
39ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
40ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport android.app.Activity;
41ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport android.content.Context;
42ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport android.content.pm.ActivityInfo;
43ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport android.graphics.SurfaceTexture;
44ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport android.media.MediaPlayer;
45ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport android.opengl.GLES20;
46ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport android.opengl.GLSurfaceView;
47ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport android.opengl.GLUtils;
48ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport android.opengl.Matrix;
49ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport android.os.Bundle;
50ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport android.util.Log;
51ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport android.view.MotionEvent;
52af4fccf0c029be1f6964de5a456ceb651f052ba4Jamie Gennisimport android.view.Surface;
53ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport android.view.SurfaceHolder;
54ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport android.view.View;
55ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport android.widget.MediaController;
56ea0bad0574451212591841ba84f477ecc216003aHuahui Wuimport android.widget.MediaController.MediaPlayerControl;
57ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
58ea0bad0574451212591841ba84f477ecc216003aHuahui Wu/**
59ea0bad0574451212591841ba84f477ecc216003aHuahui Wu * A view to play a video, specified by VideoDumpConfig.VIDEO_URI, and dump the screen
60ea0bad0574451212591841ba84f477ecc216003aHuahui Wu * into raw RGB files.
61ea0bad0574451212591841ba84f477ecc216003aHuahui Wu * It uses a renderer to display each video frame over a surface texture, read pixels,
62ea0bad0574451212591841ba84f477ecc216003aHuahui Wu * and writes the pixels into a rgb file on sdcard.
63ea0bad0574451212591841ba84f477ecc216003aHuahui Wu * Those raw rgb files will be used to compare the quality distortion against
64ea0bad0574451212591841ba84f477ecc216003aHuahui Wu * the original video. They can be viewed with the RgbPlayer app for debugging.
65ea0bad0574451212591841ba84f477ecc216003aHuahui Wu */
66ea0bad0574451212591841ba84f477ecc216003aHuahui Wuclass VideoDumpView extends GLSurfaceView implements MediaPlayerControl {
67ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    private static final String TAG = "VideoDumpView";
68ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    VideoDumpRenderer mRenderer;
69ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    private MediaController mMediaController;
70ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    private boolean mMediaControllerAttached = false;
71ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    private MediaPlayer mMediaPlayer = null;
72ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    private BufferedWriter mImageListWriter = null;
73ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
74ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    // A serials of configuration constants.
75ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    class VideoDumpConfig {
76ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        // Currently we are running with a local copy of the video.
77ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        // It should work with a "http://" sort of streaming url as well.
78ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        public static final String VIDEO_URI = "/sdcard/mediadump/sample.mp4";
79ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        public static final String ROOT_DIR = "/sdcard/mediadump/";
80ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        public static final String IMAGES_LIST = "images.lst";
81ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        public static final String IMAGE_PREFIX = "img";
82ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        public static final String IMAGE_SUFFIX = ".rgb";
83ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        public static final String PROPERTY_FILE = "prop.xml";
84ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
85ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        // So far, glReadPixels only supports two (format, type) combinations
86ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        //     GL_RGB  GL_UNSIGNED_SHORT_5_6_5   16 bits per pixel (default)
87ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        //     GL_RGBA GL_UNSIGNED_BYTE          32 bits per pixel
88ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        public static final int PIXEL_FORMAT = GLES20.GL_RGB;
89ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        public static final int PIXEL_TYPE = PIXEL_FORMAT == GLES20.GL_RGBA
90ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                ? GLES20.GL_UNSIGNED_BYTE : GLES20.GL_UNSIGNED_SHORT_5_6_5;
91ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        public static final int BYTES_PER_PIXEL =
92ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                PIXEL_FORMAT == GLES20.GL_RGBA ? 4 : 2;
93ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        public static final boolean SET_CHOOSER
94ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                = PIXEL_FORMAT == GLES20.GL_RGBA ? true : false;
95ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
96ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        // On Motorola Xoom, it takes 100ms to read pixels and 180ms to write to a file
97ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        // to dump a complete 720p(1280*720) video frame. It's much slower than the frame
98ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        // playback interval (40ms). So we only dump a center block and it should be able
99ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        // to catch all the e2e distortion. A reasonable size of the block is 256x256,
100ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        // which takes 4ms to read pixels and 25 ms to write to a file.
101ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        public static final int MAX_DUMP_WIDTH = 256;
102ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        public static final int MAX_DUMP_HEIGHT = 256;
103ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
104ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        // TODO: MediaPlayer doesn't give back the video frame rate and we'll need to
105ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        // figure it by dividing the total number of frames by the duration.
106ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        public static final int FRAME_RATE = 25;
107ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    }
108ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
109ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    public VideoDumpView(Context context) {
110ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        super(context);
111ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        setEGLContextClientVersion(2);
112ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        // GLSurfaceView uses RGB_5_6_5 by default.
113ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        if (VideoDumpConfig.SET_CHOOSER) {
114ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            setEGLConfigChooser(8, 8, 8, 8, 8, 8);
115ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        }
116ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        mRenderer = new VideoDumpRenderer(context);
117ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        setRenderer(mRenderer);
118ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    }
119ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
120ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    @Override
121ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    public void onPause() {
122ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        stopPlayback();
123ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        super.onPause();
124ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    }
125ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
126ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    @Override
127ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    public void onResume() {
128ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        Log.d(TAG, "onResume");
129ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
130ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        mMediaPlayer = new MediaPlayer();
131ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        try {
132ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            mMediaPlayer.setDataSource(VideoDumpConfig.VIDEO_URI);
133ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
134ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            class RGBFilter implements FilenameFilter {
135ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                public boolean accept(File dir, String name) {
136ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                    return (name.endsWith(VideoDumpConfig.IMAGE_SUFFIX));
137ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                }
138ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            }
139ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            File dump_dir = new File(VideoDumpConfig.ROOT_DIR);
140ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            File[] dump_files = dump_dir.listFiles(new RGBFilter());
141ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            for (File dump_file :dump_files) {
142ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                dump_file.delete();
143ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            }
144ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
145ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            File image_list = new File(VideoDumpConfig.ROOT_DIR
146ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                                       + VideoDumpConfig.IMAGES_LIST);
147ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            image_list.delete();
148ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            mImageListWriter = new BufferedWriter(new FileWriter(image_list));
149ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        } catch (java.io.IOException e) {
150ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            Log.e(TAG, e.getMessage(), e);
151ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        }
152ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
153ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        queueEvent(new Runnable(){
154ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                public void run() {
155ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                    mRenderer.setMediaPlayer(mMediaPlayer);
156ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                    mRenderer.setImageListWriter(mImageListWriter);
157ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                }});
158ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
159ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        super.onResume();
160ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    }
161ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
162ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    public void start() {
163ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        mMediaPlayer.start();
164ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    }
165ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
166ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    public void pause() {
167ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        mMediaPlayer.pause();
168ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        try {
169ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            mImageListWriter.flush();
170ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        } catch (java.io.IOException e) {
171ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            Log.e(TAG, e.getMessage(), e);
172ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        }
173ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    }
174ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
175ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    public void stopPlayback() {
176ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        Log.d(TAG, "stopPlayback");
177ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
178ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        if (mMediaPlayer != null) {
179ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            mMediaPlayer.stop();
180ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            mMediaPlayer.release();
181ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            mMediaPlayer = null;
182ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        }
183ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        if (mImageListWriter != null) {
184ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            try {
185ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                mImageListWriter.flush();
186ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                mImageListWriter.close();
187ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            } catch (java.io.IOException e) {
188ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                Log.e(TAG, e.getMessage(), e);
189ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            }
190ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        } else {
191ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            Log.d(TAG, "image list file was not written successfully.");
192ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        }
193ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    }
194ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
195ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    public void setMediaController(MediaController controller) {
196ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        if (mMediaController != null) {
197ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            mMediaController.hide();
198ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        }
199ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        mMediaController = controller;
200ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    }
201ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
202ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    private void attachMediaController() {
203ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        if (mMediaPlayer != null && mMediaController != null) {
204ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            if (!mMediaControllerAttached) {
205ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                mMediaController.setMediaPlayer(this);
206ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                View anchorView = this.getParent() instanceof View ?
207ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                        (View)this.getParent() : this;
208ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                mMediaController.setAnchorView(anchorView);
209ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                mMediaController.setEnabled(true);
210ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                mMediaControllerAttached = true;
211ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            }
212ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            mMediaController.show();
213ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        }
214ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    }
215ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
216ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    private boolean isInPlaybackState() {
217ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        return (mMediaPlayer != null && mMediaPlayer.isPlaying());
218ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    }
219ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
220ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    public boolean canPause () {
221ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        return true;
222ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    }
223ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
224ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    public boolean canSeekBackward () {
225ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        return true;
226ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    }
227ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
228ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    public boolean canSeekForward () {
229ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        return true;
230ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    }
231ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
232ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    public int getBufferPercentage () {
233ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        return 1;
234ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    }
235ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
236ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    public int getCurrentPosition () {
237ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        if (isInPlaybackState()) {
238ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            return mMediaPlayer.getCurrentPosition();
239ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        }
240ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        return 0;
241ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    }
242ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
243ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    public int getDuration () {
244ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        return mMediaPlayer.getDuration();
245ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    }
246ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
247ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    public boolean isPlaying () {
248ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        return isInPlaybackState() && mMediaPlayer.isPlaying();
249ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    }
250ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
251ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    public void seekTo (int pos) {
252ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        mMediaPlayer.seekTo(pos);
253ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    }
254ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
255ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    @Override
256804e618db6240a6c253c0d3eab2232b1823e217aMarco Nelissen    public int getAudioSessionId() {
257804e618db6240a6c253c0d3eab2232b1823e217aMarco Nelissen        return 0;
258804e618db6240a6c253c0d3eab2232b1823e217aMarco Nelissen    }
259804e618db6240a6c253c0d3eab2232b1823e217aMarco Nelissen
260804e618db6240a6c253c0d3eab2232b1823e217aMarco Nelissen    @Override
261ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    public boolean onTouchEvent(MotionEvent ev) {
262ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        attachMediaController();
263ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        return true;
264ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    }
265ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
266ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    /**
267ea0bad0574451212591841ba84f477ecc216003aHuahui Wu     * A renderer to read each video frame from a media player, draw it over a surface
268ea0bad0574451212591841ba84f477ecc216003aHuahui Wu     * texture, dump the on-screen pixels into a buffer, and writes the pixels into
269ea0bad0574451212591841ba84f477ecc216003aHuahui Wu     * a rgb file on sdcard.
270ea0bad0574451212591841ba84f477ecc216003aHuahui Wu     */
271ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    private static class VideoDumpRenderer
272ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        implements GLSurfaceView.Renderer, SurfaceTexture.OnFrameAvailableListener {
273ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private static String TAG = "VideoDumpRenderer";
274ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
275ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        /* All GL related fields from
276ea0bad0574451212591841ba84f477ecc216003aHuahui Wu         * http://developer.android.com/resources/samples/ApiDemos/src/com/example
277ea0bad0574451212591841ba84f477ecc216003aHuahui Wu         * /android/apis/graphics/GLES20TriangleRenderer.html
278ea0bad0574451212591841ba84f477ecc216003aHuahui Wu         */
279ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private static final int FLOAT_SIZE_BYTES = 4;
280ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
281ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
282ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
283ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private final float[] mTriangleVerticesData = {
284ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            // X, Y, Z, U, V
285ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            -1.0f, -1.0f, 0, 0.f, 0.f,
286ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            1.0f, -1.0f, 0, 1.f, 0.f,
287ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            -1.0f,  1.0f, 0, 0.f, 1.f,
288ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            1.0f,  1.0f, 0, 1.f, 1.f,
289ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        };
290ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
291ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private FloatBuffer mTriangleVertices;
292ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
293ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private final String mVertexShader =
294ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                "uniform mat4 uMVPMatrix;\n" +
295ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                "uniform mat4 uSTMatrix;\n" +
296ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                "attribute vec4 aPosition;\n" +
297ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                "attribute vec4 aTextureCoord;\n" +
298ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                "varying vec2 vTextureCoord;\n" +
299ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                "void main() {\n" +
300ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                "  gl_Position = uMVPMatrix * aPosition;\n" +
301ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                "  vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
302ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                "}\n";
303ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
304ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private final String mFragmentShader =
305ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                "#extension GL_OES_EGL_image_external : require\n" +
306ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                "precision mediump float;\n" +
307ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                "varying vec2 vTextureCoord;\n" +
308ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                "uniform samplerExternalOES sTexture;\n" +
309ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                "void main() {\n" +
310ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                "  gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
311ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                "}\n";
312ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
313ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private float[] mMVPMatrix = new float[16];
314ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private float[] mSTMatrix = new float[16];
315ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
316ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private int mProgram;
317ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private int mTextureID;
318ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private int muMVPMatrixHandle;
319ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private int muSTMatrixHandle;
320ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private int maPositionHandle;
321ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private int maTextureHandle;
322ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
323ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private SurfaceTexture mSurface;
324ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private boolean updateSurface = false;
325ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
326ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        // Magic key
327ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private static int GL_TEXTURE_EXTERNAL_OES = 0x8D65;
328ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
329ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
330ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        /**
331ea0bad0574451212591841ba84f477ecc216003aHuahui Wu         * Fields that reads video source and dumps to file.
332ea0bad0574451212591841ba84f477ecc216003aHuahui Wu         */
333ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        // The media player that loads and decodes the video.
334ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        // Not owned by this class.
335ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private MediaPlayer mMediaPlayer;
336ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        // The frame number from media player.
337ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private int mFrameNumber = 0;
338ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        // The frame number that is drawing on screen.
339ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private int mDrawNumber = 0;
340ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        // The width and height of dumping block.
341ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private int mWidth = 0;
342ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private int mHeight = 0;
343ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        // The offset of the dumping block.
344ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private int mStartX = 0;
345ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private int mStartY = 0;
346ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        // A buffer to hold the dumping pixels.
347ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private ByteBuffer mBuffer = null;
348ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        // A file writer to write the filenames of images.
349ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private BufferedWriter mImageListWriter;
350ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
351ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        public VideoDumpRenderer(Context context) {
352ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            mTriangleVertices = ByteBuffer.allocateDirect(
353ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                mTriangleVerticesData.length * FLOAT_SIZE_BYTES)
354ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                    .order(ByteOrder.nativeOrder()).asFloatBuffer();
355ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            mTriangleVertices.put(mTriangleVerticesData).position(0);
356ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
357ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            Matrix.setIdentityM(mSTMatrix, 0);
358ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        }
359ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
360ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        public void setMediaPlayer(MediaPlayer player) {
361ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            mMediaPlayer = player;
362ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        }
363ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
364ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        public void setImageListWriter(BufferedWriter imageListWriter) {
365ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            mImageListWriter = imageListWriter;
366ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        }
367ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
368ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        /**
369ea0bad0574451212591841ba84f477ecc216003aHuahui Wu         * Called to draw the current frame.
370ea0bad0574451212591841ba84f477ecc216003aHuahui Wu         * This method is responsible for drawing the current frame.
371ea0bad0574451212591841ba84f477ecc216003aHuahui Wu         */
372ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        public void onDrawFrame(GL10 glUnused) {
373ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            boolean isNewFrame = false;
374ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            int frameNumber = 0;
375ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
376ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            synchronized(this) {
377ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                if (updateSurface) {
378ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                    isNewFrame = true;
379ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                    frameNumber = mFrameNumber;
380ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                    mSurface.updateTexImage();
381ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                    mSurface.getTransformMatrix(mSTMatrix);
382ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                    updateSurface = false;
383ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                }
384ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            }
385ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
386ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            // Initial clear.
387ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            GLES20.glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
388ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
389ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
390ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            // Load the program, which is the basics rules to draw the vertexes and textures.
391ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            GLES20.glUseProgram(mProgram);
392ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            checkGlError("glUseProgram");
393ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
394ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            // Activate the texture.
395ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
396ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID);
397ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
398ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            // Load the vertexes coordinates. Simple here since it only draw a rectangle
399ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            // that fits the whole screen.
400ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
401ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false,
402ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
403ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            checkGlError("glVertexAttribPointer maPosition");
404ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            GLES20.glEnableVertexAttribArray(maPositionHandle);
405ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            checkGlError("glEnableVertexAttribArray maPositionHandle");
406ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
407ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            // Load the texture coordinates, which is essentially a rectangle that fits
408ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            // the whole video frame.
409ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
410ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            GLES20.glVertexAttribPointer(maTextureHandle, 3, GLES20.GL_FLOAT, false,
411ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
412ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            checkGlError("glVertexAttribPointer maTextureHandle");
413ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            GLES20.glEnableVertexAttribArray(maTextureHandle);
414ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            checkGlError("glEnableVertexAttribArray maTextureHandle");
415ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
416ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            // Set up the GL matrices.
417ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            Matrix.setIdentityM(mMVPMatrix, 0);
418ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
419ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            GLES20.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0);
420ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
421ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            // Draw a rectangle and render the video frame as a texture on it.
422ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
423ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            checkGlError("glDrawArrays");
424ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            GLES20.glFinish();
425ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
426ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            if (isNewFrame) {  // avoid duplicates.
427ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                Log.d(TAG, mDrawNumber + "/" + frameNumber + " before dumping "
428ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                      + System.currentTimeMillis());
429ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                DumpToFile(frameNumber);
430ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                Log.d(TAG, mDrawNumber + "/" + frameNumber + " after  dumping "
431ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                      + System.currentTimeMillis());
432ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
433ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                mDrawNumber++;
434ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            }
435ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        }
436ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
437ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        // Call the GL function that dumps the screen into a buffer, then write to a file.
438ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private void DumpToFile(int frameNumber) {
439ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            GLES20.glReadPixels(mStartX, mStartY, mWidth, mHeight,
440ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                                VideoDumpConfig.PIXEL_FORMAT,
441ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                                VideoDumpConfig.PIXEL_TYPE,
442ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                                mBuffer);
443ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            checkGlError("glReadPixels");
444ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
445ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            Log.d(TAG, mDrawNumber + "/" + frameNumber + " after  glReadPixels "
446ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                  + System.currentTimeMillis());
447ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
448ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            String filename =  VideoDumpConfig.ROOT_DIR + VideoDumpConfig.IMAGE_PREFIX
449ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                    + frameNumber + VideoDumpConfig.IMAGE_SUFFIX;
450ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            try {
451ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                mImageListWriter.write(filename);
452ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                mImageListWriter.newLine();
453ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                FileOutputStream fos = new FileOutputStream(filename);
454ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                fos.write(mBuffer.array());
455ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                fos.close();
456ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            } catch (java.io.IOException e) {
457ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                Log.e(TAG, e.getMessage(), e);
458ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            }
459ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        }
460ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
461ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        /**
462ea0bad0574451212591841ba84f477ecc216003aHuahui Wu         * Called when the surface changed size.
463ea0bad0574451212591841ba84f477ecc216003aHuahui Wu         * Called after the surface is created and whenever the OpenGL surface size changes.
464ea0bad0574451212591841ba84f477ecc216003aHuahui Wu         */
465ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        public void onSurfaceChanged(GL10 glUnused, int width, int height) {
466ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            Log.d(TAG, "Surface size: " + width + "x" + height);
467ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
468ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            int video_width = mMediaPlayer.getVideoWidth();
469ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            int video_height = mMediaPlayer.getVideoHeight();
470ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            Log.d(TAG, "Video size: " + video_width
471ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                  + "x" + video_height);
472ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
473ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            // TODO: adjust video_width and video_height with the surface size.
474ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            GLES20.glViewport(0, 0, video_width, video_height);
475ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
476ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            mWidth = Math.min(VideoDumpConfig.MAX_DUMP_WIDTH, video_width);
477ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            mHeight = Math.min(VideoDumpConfig.MAX_DUMP_HEIGHT, video_height);
478ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            mStartX = video_width / mWidth / 2 * mWidth;
479ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            mStartY = video_height / mHeight / 2 * mHeight;
480ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
481ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            Log.d(TAG, "dumping block start at (" + mStartX + "," + mStartY + ") "
482ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                  + "size " + mWidth + "x" + mHeight);
483ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
484ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            int image_size = mWidth * mHeight * VideoDumpConfig.BYTES_PER_PIXEL;
485ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            mBuffer = ByteBuffer.allocate(image_size);
486ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
487ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            int bpp[] = new int[3];
488ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            GLES20.glGetIntegerv(GLES20.GL_RED_BITS, bpp, 0);
489ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            GLES20.glGetIntegerv(GLES20.GL_GREEN_BITS, bpp, 1);
490ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            GLES20.glGetIntegerv(GLES20.GL_BLUE_BITS, bpp, 2);
491ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            Log.d(TAG, "rgb bits: " + bpp[0] + "-" + bpp[1] + "-" + bpp[2]);
492ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
493ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            // Save the properties into a xml file
494ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            // so the RgbPlayer can understand the output format.
495ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            Properties prop = new Properties();
496ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            prop.setProperty("width", Integer.toString(mWidth));
497ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            prop.setProperty("height", Integer.toString(mHeight));
498ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            prop.setProperty("startX", Integer.toString(mStartX));
499ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            prop.setProperty("startY", Integer.toString(mStartY));
500ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            prop.setProperty("bytesPerPixel",
501ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                             Integer.toString(VideoDumpConfig.BYTES_PER_PIXEL));
502ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            prop.setProperty("frameRate", Integer.toString(VideoDumpConfig.FRAME_RATE));
503ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            try {
504ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                prop.storeToXML(new FileOutputStream(VideoDumpConfig.ROOT_DIR
505ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                                                     + VideoDumpConfig.PROPERTY_FILE), "");
506ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            } catch (java.io.IOException e) {
507ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                Log.e(TAG, e.getMessage(), e);
508ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            }
509ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        }
510ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
511ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        /**
512ea0bad0574451212591841ba84f477ecc216003aHuahui Wu         * Called when the surface is created or recreated.
513ea0bad0574451212591841ba84f477ecc216003aHuahui Wu         * Called when the rendering thread starts and whenever the EGL context is lost.
514ea0bad0574451212591841ba84f477ecc216003aHuahui Wu         * A place to put code to create resources that need to be created when the rendering
515ea0bad0574451212591841ba84f477ecc216003aHuahui Wu         * starts, and that need to be recreated when the EGL context is lost e.g. texture.
516ea0bad0574451212591841ba84f477ecc216003aHuahui Wu         * Note that when the EGL context is lost, all OpenGL resources associated with
517ea0bad0574451212591841ba84f477ecc216003aHuahui Wu         * that context will be automatically deleted.
518ea0bad0574451212591841ba84f477ecc216003aHuahui Wu         */
519ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
520ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            Log.d(TAG, "onSurfaceCreated");
521ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
522ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            /* Set up shaders and handles to their variables */
523ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            mProgram = createProgram(mVertexShader, mFragmentShader);
524ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            if (mProgram == 0) {
525ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                return;
526ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            }
527ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
528ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            checkGlError("glGetAttribLocation aPosition");
529ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            if (maPositionHandle == -1) {
530ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                throw new RuntimeException("Could not get attrib location for aPosition");
531ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            }
532ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord");
533ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            checkGlError("glGetAttribLocation aTextureCoord");
534ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            if (maTextureHandle == -1) {
535ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                throw new RuntimeException("Could not get attrib location for aTextureCoord");
536ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            }
537ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
538ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
539ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            checkGlError("glGetUniformLocation uMVPMatrix");
540ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            if (muMVPMatrixHandle == -1) {
541ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                throw new RuntimeException("Could not get attrib location for uMVPMatrix");
542ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            }
543ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
544ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            muSTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix");
545ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            checkGlError("glGetUniformLocation uSTMatrix");
546ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            if (muSTMatrixHandle == -1) {
547ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                throw new RuntimeException("Could not get attrib location for uSTMatrix");
548ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            }
549ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
550ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
551ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            // Create our texture. This has to be done each time the surface is created.
552ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            int[] textures = new int[1];
553ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            GLES20.glGenTextures(1, textures, 0);
554ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
555ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            mTextureID = textures[0];
556ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID);
557ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            checkGlError("glBindTexture mTextureID");
558ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
559ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            // Can't do mipmapping with mediaplayer source
560ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
561ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                                   GLES20.GL_NEAREST);
562ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
563ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                                   GLES20.GL_LINEAR);
564ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            // Clamp to edge is the only option
565ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S,
566ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                                   GLES20.GL_CLAMP_TO_EDGE);
567ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T,
568ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                                   GLES20.GL_CLAMP_TO_EDGE);
569ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            checkGlError("glTexParameteri mTextureID");
570ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
571ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            /*
572ea0bad0574451212591841ba84f477ecc216003aHuahui Wu             * Create the SurfaceTexture that will feed this textureID,
573ea0bad0574451212591841ba84f477ecc216003aHuahui Wu             * and pass it to the MediaPlayer
574ea0bad0574451212591841ba84f477ecc216003aHuahui Wu             */
575ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            mSurface = new SurfaceTexture(mTextureID);
576ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            mSurface.setOnFrameAvailableListener(this);
577ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
578af4fccf0c029be1f6964de5a456ceb651f052ba4Jamie Gennis            Surface surface = new Surface(mSurface);
579af4fccf0c029be1f6964de5a456ceb651f052ba4Jamie Gennis            mMediaPlayer.setSurface(surface);
580af4fccf0c029be1f6964de5a456ceb651f052ba4Jamie Gennis            surface.release();
581ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
582ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            try {
583ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                mMediaPlayer.prepare();
584ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            } catch (IOException t) {
585ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                Log.e(TAG, "media player prepare failed");
586ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            }
587ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
588ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            synchronized(this) {
589ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                updateSurface = false;
590ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            }
591ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        }
592ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
593ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        synchronized public void onFrameAvailable(SurfaceTexture surface) {
594ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            /* For simplicity, SurfaceTexture calls here when it has new
595ea0bad0574451212591841ba84f477ecc216003aHuahui Wu             * data available.  Call may come in from some random thread,
596ea0bad0574451212591841ba84f477ecc216003aHuahui Wu             * so let's be safe and use synchronize. No OpenGL calls can be done here.
597ea0bad0574451212591841ba84f477ecc216003aHuahui Wu             */
598ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            mFrameNumber++;
599ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            updateSurface = true;
600ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        }
601ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
602ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private int loadShader(int shaderType, String source) {
603ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            int shader = GLES20.glCreateShader(shaderType);
604ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            if (shader != 0) {
605ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                GLES20.glShaderSource(shader, source);
606ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                GLES20.glCompileShader(shader);
607ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                int[] compiled = new int[1];
608ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
609ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                if (compiled[0] == 0) {
610ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                    Log.e(TAG, "Could not compile shader " + shaderType + ":");
611ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                    Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
612ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                    GLES20.glDeleteShader(shader);
613ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                    shader = 0;
614ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                }
615ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            }
616ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            return shader;
617ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        }
618ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
619ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private int createProgram(String vertexSource, String fragmentSource) {
620ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
621ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            if (vertexShader == 0) {
622ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                return 0;
623ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            }
624ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
625ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            if (pixelShader == 0) {
626ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                return 0;
627ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            }
628ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
629ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            int program = GLES20.glCreateProgram();
630ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            if (program != 0) {
631ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                GLES20.glAttachShader(program, vertexShader);
632ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                checkGlError("glAttachShader");
633ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                GLES20.glAttachShader(program, pixelShader);
634ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                checkGlError("glAttachShader");
635ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                GLES20.glLinkProgram(program);
636ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                int[] linkStatus = new int[1];
637ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
638ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                if (linkStatus[0] != GLES20.GL_TRUE) {
639ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                    Log.e(TAG, "Could not link program: ");
640ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                    Log.e(TAG, GLES20.glGetProgramInfoLog(program));
641ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                    GLES20.glDeleteProgram(program);
642ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                    program = 0;
643ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                }
644ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            }
645ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            return program;
646ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        }
647ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
648ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        private void checkGlError(String op) {
649ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            int error;
650ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
651ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                Log.e(TAG, op + ": glError " + error);
652ea0bad0574451212591841ba84f477ecc216003aHuahui Wu                throw new RuntimeException(op + ": glError " + error);
653ea0bad0574451212591841ba84f477ecc216003aHuahui Wu            }
654ea0bad0574451212591841ba84f477ecc216003aHuahui Wu        }
655ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
656ea0bad0574451212591841ba84f477ecc216003aHuahui Wu    }  // End of class VideoDumpRender.
657ea0bad0574451212591841ba84f477ecc216003aHuahui Wu
658ea0bad0574451212591841ba84f477ecc216003aHuahui Wu}  // End of class VideoDumpView.
659