| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.media.cts; |
| |
| import com.android.cts.media.R; |
| |
| import android.content.Context; |
| import android.content.res.AssetFileDescriptor; |
| import android.content.res.Resources; |
| import android.cts.util.DeviceReportLog; |
| import android.cts.util.MediaUtils; |
| import android.media.MediaCodec; |
| import android.media.MediaCodecList; |
| import android.media.MediaCodecInfo; |
| import android.media.MediaCodecInfo.CodecCapabilities; |
| import android.media.MediaExtractor; |
| import android.media.MediaFormat; |
| import android.util.Log; |
| import android.view.Surface; |
| |
| import com.android.cts.util.ReportLog; |
| import com.android.cts.util.ResultType; |
| import com.android.cts.util.ResultUnit; |
| |
| import java.nio.ByteBuffer; |
| import java.util.Arrays; |
| import java.util.ArrayList; |
| |
| public class VideoDecoderPerfTest extends MediaPlayerTestBase { |
| private static final String TAG = "VideoDecoderPerfTest"; |
| private static final int TOTAL_FRAMES = 3000; |
| private static final int NUMBER_OF_REPEAT = 2; |
| private static final String VIDEO_AVC = MediaFormat.MIMETYPE_VIDEO_AVC; |
| private static final String VIDEO_VP8 = MediaFormat.MIMETYPE_VIDEO_VP8; |
| private static final String VIDEO_VP9 = MediaFormat.MIMETYPE_VIDEO_VP9; |
| private static final String VIDEO_HEVC = MediaFormat.MIMETYPE_VIDEO_HEVC; |
| private static final String VIDEO_H263 = MediaFormat.MIMETYPE_VIDEO_H263; |
| private static final String VIDEO_MPEG4 = MediaFormat.MIMETYPE_VIDEO_MPEG4; |
| |
| private Resources mResources; |
| private DeviceReportLog mReportLog = new DeviceReportLog(); |
| |
| @Override |
| protected void setUp() throws Exception { |
| super.setUp(); |
| mResources = mContext.getResources(); |
| } |
| |
| private static String[] getDecoderName(String mime) { |
| MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS); |
| ArrayList<String> result = new ArrayList<String>(); |
| for (MediaCodecInfo info : mcl.getCodecInfos()) { |
| if (info.isEncoder()) { |
| continue; |
| } |
| CodecCapabilities caps = null; |
| try { |
| caps = info.getCapabilitiesForType(mime); |
| } catch (IllegalArgumentException e) { // mime is not supported |
| continue; |
| } |
| result.add(info.getName()); |
| } |
| return result.toArray(new String[result.size()]); |
| } |
| |
| private void decode(String mime, int video, int width, int height) throws Exception { |
| String[] names = getDecoderName(mime); |
| for (String name: names) { |
| if (!MediaUtils.supports(name, mime, width, height)) { |
| Log.i(TAG, "Codec " + name + " with " + width + "," + height + " not supported"); |
| continue; |
| } |
| |
| Log.d(TAG, "testing " + name); |
| for (int i = 0; i < NUMBER_OF_REPEAT; ++i) { |
| // Decode to Surface. |
| Log.d(TAG, "round #" + i + " decode to surface"); |
| Surface s = getActivity().getSurfaceHolder().getSurface(); |
| doDecode(name, video, TOTAL_FRAMES, s); |
| |
| // Decode to buffer. |
| Log.d(TAG, "round #" + i + " decode to buffer"); |
| doDecode(name, video, TOTAL_FRAMES, null); |
| } |
| } |
| } |
| |
| private void doDecode(String name, int video, int stopAtSample, Surface surface) |
| throws Exception { |
| AssetFileDescriptor testFd = mResources.openRawResourceFd(video); |
| MediaExtractor extractor = new MediaExtractor(); |
| extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(), |
| testFd.getLength()); |
| extractor.selectTrack(0); |
| |
| int trackIndex = extractor.getSampleTrackIndex(); |
| MediaFormat format = extractor.getTrackFormat(trackIndex); |
| String mime = format.getString(MediaFormat.KEY_MIME); |
| ByteBuffer[] codecInputBuffers; |
| ByteBuffer[] codecOutputBuffers; |
| |
| MediaCodec codec = MediaCodec.createByCodecName(name); |
| codec.configure(format, surface, null /* crypto */, 0 /* flags */); |
| codec.start(); |
| codecInputBuffers = codec.getInputBuffers(); |
| codecOutputBuffers = codec.getOutputBuffers(); |
| |
| // start decode loop |
| MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); |
| |
| final long kTimeOutUs = 5000; // 5ms timeout |
| long[] frameTimeDiff = new long[TOTAL_FRAMES - 1]; |
| long lastOutputTimeNs = 0; |
| boolean sawInputEOS = false; |
| boolean sawOutputEOS = false; |
| int inputNum = 0; |
| int outputNum = 0; |
| int width = 0; |
| int height = 0; |
| long start = System.currentTimeMillis(); |
| while (!sawOutputEOS) { |
| // handle input |
| if (!sawInputEOS) { |
| int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs); |
| |
| if (inputBufIndex >= 0) { |
| ByteBuffer dstBuf = codecInputBuffers[inputBufIndex]; |
| |
| int sampleSize = |
| extractor.readSampleData(dstBuf, 0 /* offset */); |
| if (sampleSize < 0) { |
| // repeat from beginning. |
| extractor.release(); |
| extractor = new MediaExtractor(); |
| extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(), |
| testFd.getLength()); |
| extractor.selectTrack(0); |
| sampleSize = |
| extractor.readSampleData(dstBuf, 0 /* offset */); |
| } |
| assert sampleSize >= 0: |
| "extractor.readSampleData returns negative."; |
| long presentationTimeUs = extractor.getSampleTime(); |
| if (++inputNum == stopAtSample) { |
| Log.d(TAG, "saw input EOS (stop at sample)."); |
| sawInputEOS = true; // tag this sample as EOS |
| } |
| codec.queueInputBuffer( |
| inputBufIndex, |
| 0 /* offset */, |
| sampleSize, |
| presentationTimeUs, |
| sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); |
| } else { |
| assertEquals( |
| "codec.dequeueInputBuffer() unrecognized return value: " + inputBufIndex, |
| MediaCodec.INFO_TRY_AGAIN_LATER, inputBufIndex); |
| } |
| } |
| |
| // handle output |
| int outputBufIndex = codec.dequeueOutputBuffer(info, kTimeOutUs); |
| |
| if (outputBufIndex >= 0) { |
| if (info.size > 0) { // Disregard 0-sized buffers at the end. |
| if (lastOutputTimeNs > 0) { |
| frameTimeDiff[outputNum - 1] = System.nanoTime() - lastOutputTimeNs; |
| } |
| lastOutputTimeNs = System.nanoTime(); |
| outputNum++; |
| } |
| codec.releaseOutputBuffer(outputBufIndex, false /* render */); |
| if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { |
| Log.d(TAG, "saw output EOS."); |
| sawOutputEOS = true; |
| } |
| } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { |
| codecOutputBuffers = codec.getOutputBuffers(); |
| Log.d(TAG, "output buffers have changed."); |
| } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { |
| MediaFormat oformat = codec.getOutputFormat(); |
| width = oformat.getInteger(MediaFormat.KEY_WIDTH); |
| height = oformat.getInteger(MediaFormat.KEY_HEIGHT); |
| Log.d(TAG, "output resolution " + width + "x" + height); |
| } else { |
| assertEquals( |
| "codec.dequeueOutputBuffer() unrecognized return index: " |
| + outputBufIndex, |
| MediaCodec.INFO_TRY_AGAIN_LATER, outputBufIndex); |
| } |
| } |
| long finish = System.currentTimeMillis(); |
| |
| codec.stop(); |
| codec.release(); |
| |
| extractor.release(); |
| testFd.close(); |
| |
| Log.d(TAG, "input num " + inputNum + " vs output num " + outputNum); |
| |
| String testConfig = "codec=" + name + |
| " decodeto=" + ((surface == null) ? "buffer" : "surface") + |
| " size=" + width + "x" + height; |
| |
| String message = "average fps for " + testConfig; |
| double fps = (double)outputNum / ((finish - start) / 1000.0); |
| mReportLog.printValue(message, fps, ResultType.HIGHER_BETTER, ResultUnit.FPS); |
| |
| message = "frame time diff for " + testConfig + ": " + Arrays.toString(frameTimeDiff); |
| mReportLog.printValue(message, 0, ResultType.NEUTRAL, ResultUnit.NONE); |
| } |
| |
| public void testH264320x240() throws Exception { |
| decode(VIDEO_AVC, |
| R.raw.video_320x240_mp4_h264_800kbps_30fps_aac_stereo_128kbps_44100hz, |
| 320, 240); |
| } |
| |
| public void testH264720x480() throws Exception { |
| decode(VIDEO_AVC, |
| R.raw.video_720x480_mp4_h264_2048kbps_30fps_aac_stereo_128kbps_44100hz, |
| 720, 480); |
| } |
| |
| public void testH2641280x720() throws Exception { |
| decode(VIDEO_AVC, |
| R.raw.video_1280x720_mp4_h264_8192kbps_30fps_aac_stereo_128kbps_44100hz, |
| 1280, 720); |
| } |
| |
| public void testH2641920x1080() throws Exception { |
| decode(VIDEO_AVC, |
| R.raw.video_1920x1080_mp4_h264_20480kbps_30fps_aac_stereo_128kbps_44100hz, |
| 1920, 1080); |
| } |
| |
| public void testVP8320x240() throws Exception { |
| decode(VIDEO_VP8, |
| R.raw.video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_44100hz, |
| 320, 240); |
| } |
| |
| public void testVP8640x360() throws Exception { |
| decode(VIDEO_VP8, |
| R.raw.video_640x360_webm_vp8_2048kbps_30fps_vorbis_stereo_128kbps_48000hz, |
| 640, 360); |
| } |
| |
| public void testVP81280x720() throws Exception { |
| decode(VIDEO_VP8, |
| R.raw.video_1280x720_webm_vp8_8192kbps_30fps_vorbis_stereo_128kbps_48000hz, |
| 1280, 720); |
| } |
| |
| public void testVP81920x1080() throws Exception { |
| decode(VIDEO_VP8, |
| R.raw.video_1920x1080_webm_vp8_20480kbps_30fps_vorbis_stereo_128kbps_48000hz, |
| 1920, 1080); |
| } |
| |
| public void testVP9320x240() throws Exception { |
| decode(VIDEO_VP9, |
| R.raw.video_320x240_webm_vp9_600kbps_30fps_vorbis_stereo_128kbps_48000hz, |
| 320, 240); |
| } |
| |
| public void testVP9640x360() throws Exception { |
| decode(VIDEO_VP9, |
| R.raw.video_640x360_webm_vp9_1600kbps_30fps_vorbis_stereo_128kbps_48000hz, |
| 640, 360); |
| } |
| |
| public void testVP91280x720() throws Exception { |
| decode(VIDEO_VP9, |
| R.raw.video_1280x720_webm_vp9_4096kbps_30fps_vorbis_stereo_128kbps_44100hz, |
| 1280, 720); |
| } |
| |
| public void testVP91920x1080() throws Exception { |
| decode(VIDEO_VP9, |
| R.raw.video_1920x1080_webm_vp9_10240kbps_30fps_vorbis_stereo_128kbps_48000hz, |
| 1920, 1080); |
| } |
| |
| public void testVP93840x2160() throws Exception { |
| decode(VIDEO_VP9, |
| R.raw.video_3840x2160_webm_vp9_20480kbps_30fps_vorbis_stereo_128kbps_48000hz, |
| 3840, 2160); |
| } |
| |
| public void testHEVC352x288() throws Exception { |
| decode(VIDEO_HEVC, |
| R.raw.video_352x288_mp4_hevc_600kbps_30fps_aac_stereo_128kbps_44100hz, |
| 352, 288); |
| } |
| |
| public void testHEVC720x480() throws Exception { |
| decode(VIDEO_HEVC, |
| R.raw.video_720x480_mp4_hevc_1638kbps_30fps_aac_stereo_128kbps_44100hz, |
| 720, 480); |
| } |
| |
| public void testHEVC1280x720() throws Exception { |
| decode(VIDEO_HEVC, |
| R.raw.video_1280x720_mp4_hevc_4096kbps_30fps_aac_stereo_128kbps_44100hz, |
| 1280, 720); |
| } |
| |
| public void testHEVC1920x1080() throws Exception { |
| decode(VIDEO_HEVC, |
| R.raw.video_1920x1080_mp4_hevc_10240kbps_30fps_aac_stereo_128kbps_44100hz, |
| 1920, 1080); |
| } |
| |
| public void testHEVC3840x2160() throws Exception { |
| decode(VIDEO_HEVC, |
| R.raw.video_3840x2160_mp4_hevc_20480kbps_30fps_aac_stereo_128kbps_44100hz, |
| 3840, 2160); |
| } |
| |
| public void testH263176x144() throws Exception { |
| decode(VIDEO_HEVC, |
| R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, |
| 176, 144); |
| } |
| |
| public void testH263352x288() throws Exception { |
| decode(VIDEO_HEVC, |
| R.raw.video_352x288_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, |
| 352, 288); |
| } |
| |
| public void testMPEG4480x360() throws Exception { |
| decode(VIDEO_MPEG4, |
| R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, |
| 480, 360); |
| } |
| |
| public void testMPEG41280x720() throws Exception { |
| decode(VIDEO_MPEG4, |
| R.raw.video_1280x720_mp4_mpeg4_1000kbps_25fps_aac_stereo_128kbps_44100hz, |
| 1280, 720); |
| } |
| } |
| |