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