| package org.linaro.mmtest; |
| |
| import android.app.Activity; |
| import android.os.Bundle; |
| import android.os.Build; |
| import android.view.Window; |
| import android.util.Log; |
| import android.media.AudioManager; |
| import android.media.MediaPlayer; |
| import android.media.MediaPlayer.OnCompletionListener; |
| import android.os.Process; |
| import android.view.SurfaceView; |
| import android.app.AlertDialog; |
| |
| import java.io.RandomAccessFile; |
| import java.lang.Math; |
| import java.util.List; |
| |
| public class MultimediaTest extends Activity implements android.media.MediaPlayer.OnCompletionListener,android.media.MediaPlayer.OnErrorListener,android.media.MediaPlayer.OnInfoListener { |
| private static final String TAG = "MultimediaTest"; |
| private int currentTest = -1; |
| private boolean ranAnyTest = false; |
| private MediaPlayer mp; |
| private String failure; |
| private long timestamp; |
| private long cpuTimestamp; |
| private int expectedDuration; |
| private final Test[] tests = { |
| // Audio Codecs |
| new Test("test-AAC-Stereo-96k.m4a", "AAC Stereo 96k", false), |
| new Test("test-AAC-Mono-8k.m4a", "AAC Mono 8k", false), |
| new Test("test-AMR-NB.3gp", "AMR-NB", false), |
| new Test("test-flac.flac", "FLAC", false, 12 /*android.os.Build.HONEYCOMB_MR1*/), |
| new Test("test-MP3.mp3", "MP3", false), |
| new Test("test-Vorbis.ogg", "Ogg Vorbis", false), |
| new Test("test-PCM8.wav", "PCM 8-Bit", false), |
| new Test("test-PCM16.wav", "PCM 16-Bit", false), |
| |
| // Video Codecs |
| new Test("test-H.263.3gp", "H.263"), |
| new Test("test-H.264.m4v", "H.264"), |
| new Test("test-MPEG4_SP.mp4", "MPEG-4 SP"), |
| new Test("test-VP8.webm", "VP8"), |
| |
| // Mixed A/V |
| new Test("test-H.263-AAC.3gp", "3GP(H.263/AAC)"), |
| new Test("test-H.264-AAC.mp4", "MP4(H.264/AAC)"), |
| new Test("test-H.264-AAC-HD.mp4", "HD-MP4(H.264/AAC)"), |
| new Test("test-MPEG4-MP3.mp4", "MP4(MPEG4/MP3)"), |
| new Test("test-VP8-Vorbis.webm", "WebM(VP8/Vorbis)", true, 10 /*android.os.Build.GINGERBREAD_MR1*/) |
| }; |
| |
| class Test { |
| public String filename; |
| public String name; |
| public boolean hasVideo; |
| public int minVersion; |
| |
| public Test(String pFilename, String pName, boolean pHasVideo, int pMinVersion) { |
| filename = pFilename; |
| name = pName; |
| hasVideo = pHasVideo; |
| minVersion = pMinVersion; |
| } |
| |
| public Test(String pFilename, String pName, boolean pHasVideo) { |
| this(pFilename, pName, pHasVideo, 0); |
| } |
| |
| public Test(String pFilename, String pName) { |
| this(pFilename, pName, true, 0); |
| } |
| } |
| |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| Log.v(TAG, "Starting tests"); |
| |
| SurfaceView sv=new SurfaceView(this); |
| setContentView(sv); |
| |
| mp=new MediaPlayer(); |
| mp.setOnCompletionListener(this); |
| mp.setOnErrorListener(this); |
| mp.setOnInfoListener(this); |
| mp.setAudioStreamType(android.media.AudioManager.STREAM_MUSIC); |
| mp.setDisplay(sv.getHolder()); |
| |
| runNextTest(); |
| } |
| |
| public void runNextTest() { |
| failure = ""; |
| if(++currentTest > tests.length) { |
| Log.v(TAG, "Tests completed"); |
| if(!ranAnyTest) { |
| AlertDialog dlg=new AlertDialog.Builder(this).setTitle("Files not found").setMessage("Multimedia files to be tested could not be found on the SD card. Please put the files (generated by the creator script) into the /mmtest directory of the SD card and try again.").create(); |
| dlg.show(); |
| } |
| finish(); |
| } |
| Test t=tests[currentTest]; |
| if(t.minVersion > android.os.Build.VERSION.SDK_INT) { |
| Log.i(TAG, "SKIP:" + t.name + ":Not expected to pass on this version of Android"); |
| runNextTest(); |
| return; |
| } |
| Log.v(TAG, "Running test " + t.name); |
| mp.reset(); |
| try { |
| mp.setDataSource("/sdcard/mmtest/" + t.filename); |
| mp.prepare(); |
| ranAnyTest = true; |
| } catch(java.io.IOException e) { |
| Log.e(TAG, "ERR:" + tests[currentTest].name + ":test:IO Error - test file seems to be missing: /sdcard/mmtest/" + t.filename); |
| runNextTest(); |
| return; |
| } |
| if((mp.getVideoHeight() == 0) ^ t.hasVideo) |
| Log.i(TAG, "PASS:" + tests[currentTest].name + ":sanityCheck:Test and Player agree about whether or not the content has a video part"); |
| else |
| Log.i(TAG, "FAIL:" + tests[currentTest].name + ":sanityCheck:Test and Player disagree about whether or not the content has a video part"); |
| timestamp = System.currentTimeMillis(); |
| cpuTimestamp = cpuTime(); |
| expectedDuration = mp.getDuration(); |
| if(expectedDuration <= 0) |
| Log.i(TAG, "WARN:" + tests[currentTest].name + ":Couldn't determine duration"); |
| mp.start(); |
| } |
| |
| @Override |
| public void onCompletion(MediaPlayer mp) { |
| long elapsedTime = System.currentTimeMillis() - timestamp; |
| long elapsedCpuTime = cpuTime() - cpuTimestamp; |
| if(failure.length() == 0) { |
| if(expectedDuration >= 0 && Math.abs(elapsedTime - expectedDuration) > 10000) { |
| Log.i(TAG, "WARN:" + tests[currentTest].name + ":Actual playing time (" + Long.toString(elapsedTime) + "ms) differs significantly from expected time (" + Integer.toString(expectedDuration) + "ms)"); |
| } |
| Log.i(TAG, "PASS:" + tests[currentTest].name + ":playback"); |
| } else |
| Log.i(TAG, "FAIL:" + tests[currentTest].name + ":playback:" + failure); |
| // FIXME we should find a reasonable threshold to make |
| // an educated guess on whether or not a codec is hardware |
| // accelerated. |
| // Will require some testing on different hardware... |
| Log.i(TAG, "PERF:" + tests[currentTest].name + Long.toString(elapsedCpuTime) + " jiffies used"); |
| runNextTest(); |
| } |
| |
| @Override |
| public boolean onError(MediaPlayer mp, int what, int extra) { |
| failure = "Got error code " + Integer.toString(what) + "/" + Integer.toString(extra); |
| return false; // call onCompletion |
| } |
| |
| @Override |
| public boolean onInfo(MediaPlayer mp, int what, int extra) { |
| Log.i(TAG, "NOTE:" + tests[currentTest].name + ":playback:MediaPlayer sent info " + Integer.toString(what) + "/" + Integer.toString(extra)); |
| return true; |
| } |
| |
| public long cpuTime() { |
| long usedTime = 0; |
| try { |
| RandomAccessFile reader = new RandomAccessFile("/proc/" + Integer.toString(Process.myPid()) + "/stat", "r"); |
| String[] toks=reader.readLine().split(" "); |
| // 13: jiffies in user mode |
| // 14: jiffies in kernel mode |
| // 15: jiffies in user mode of child processes (maybe some codecs launch an external decoder...) |
| // 16: jiffies in kernel mode of child processes |
| usedTime = Long.parseLong(toks[13]) + Long.parseLong(toks[14]) + Long.parseLong(toks[15]) + Long.parseLong(toks[16]); |
| reader.close(); |
| } catch(java.io.FileNotFoundException e) { |
| Log.e(TAG, "ERR:/proc not mounted?"); |
| } catch(java.io.IOException e) { |
| Log.e(TAG, "ERR:I/O error while reading CPU load"); |
| } |
| return usedTime; |
| } |
| } |